diff options
Diffstat (limited to 'vendor/github.com/spf13')
26 files changed, 3971 insertions, 0 deletions
diff --git a/vendor/github.com/spf13/afero/.travis.yml b/vendor/github.com/spf13/afero/.travis.yml new file mode 100644 index 0000000..0637db7 --- /dev/null +++ b/vendor/github.com/spf13/afero/.travis.yml | |||
@@ -0,0 +1,21 @@ | |||
1 | sudo: false | ||
2 | language: go | ||
3 | |||
4 | go: | ||
5 | - 1.9 | ||
6 | - "1.10" | ||
7 | - tip | ||
8 | |||
9 | os: | ||
10 | - linux | ||
11 | - osx | ||
12 | |||
13 | matrix: | ||
14 | allow_failures: | ||
15 | - go: tip | ||
16 | fast_finish: true | ||
17 | |||
18 | script: | ||
19 | - go build | ||
20 | - go test -race -v ./... | ||
21 | |||
diff --git a/vendor/github.com/spf13/afero/LICENSE.txt b/vendor/github.com/spf13/afero/LICENSE.txt new file mode 100644 index 0000000..298f0e2 --- /dev/null +++ b/vendor/github.com/spf13/afero/LICENSE.txt | |||
@@ -0,0 +1,174 @@ | |||
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. | ||
diff --git a/vendor/github.com/spf13/afero/README.md b/vendor/github.com/spf13/afero/README.md new file mode 100644 index 0000000..0c9b04b --- /dev/null +++ b/vendor/github.com/spf13/afero/README.md | |||
@@ -0,0 +1,452 @@ | |||
1 | ![afero logo-sm](https://cloud.githubusercontent.com/assets/173412/11490338/d50e16dc-97a5-11e5-8b12-019a300d0fcb.png) | ||
2 | |||
3 | A FileSystem Abstraction System for Go | ||
4 | |||
5 | [![Build Status](https://travis-ci.org/spf13/afero.svg)](https://travis-ci.org/spf13/afero) [![Build status](https://ci.appveyor.com/api/projects/status/github/spf13/afero?branch=master&svg=true)](https://ci.appveyor.com/project/spf13/afero) [![GoDoc](https://godoc.org/github.com/spf13/afero?status.svg)](https://godoc.org/github.com/spf13/afero) [![Join the chat at https://gitter.im/spf13/afero](https://badges.gitter.im/Dev%20Chat.svg)](https://gitter.im/spf13/afero?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) | ||
6 | |||
7 | # Overview | ||
8 | |||
9 | Afero is an filesystem framework providing a simple, uniform and universal API | ||
10 | interacting with any filesystem, as an abstraction layer providing interfaces, | ||
11 | types and methods. Afero has an exceptionally clean interface and simple design | ||
12 | without needless constructors or initialization methods. | ||
13 | |||
14 | Afero is also a library providing a base set of interoperable backend | ||
15 | filesystems that make it easy to work with afero while retaining all the power | ||
16 | and benefit of the os and ioutil packages. | ||
17 | |||
18 | Afero provides significant improvements over using the os package alone, most | ||
19 | notably the ability to create mock and testing filesystems without relying on the disk. | ||
20 | |||
21 | It is suitable for use in a any situation where you would consider using the OS | ||
22 | package as it provides an additional abstraction that makes it easy to use a | ||
23 | memory backed file system during testing. It also adds support for the http | ||
24 | filesystem for full interoperability. | ||
25 | |||
26 | |||
27 | ## Afero Features | ||
28 | |||
29 | * A single consistent API for accessing a variety of filesystems | ||
30 | * Interoperation between a variety of file system types | ||
31 | * A set of interfaces to encourage and enforce interoperability between backends | ||
32 | * An atomic cross platform memory backed file system | ||
33 | * Support for compositional (union) file systems by combining multiple file systems acting as one | ||
34 | * Specialized backends which modify existing filesystems (Read Only, Regexp filtered) | ||
35 | * A set of utility functions ported from io, ioutil & hugo to be afero aware | ||
36 | |||
37 | |||
38 | # Using Afero | ||
39 | |||
40 | Afero is easy to use and easier to adopt. | ||
41 | |||
42 | A few different ways you could use Afero: | ||
43 | |||
44 | * Use the interfaces alone to define you own file system. | ||
45 | * Wrap for the OS packages. | ||
46 | * Define different filesystems for different parts of your application. | ||
47 | * Use Afero for mock filesystems while testing | ||
48 | |||
49 | ## Step 1: Install Afero | ||
50 | |||
51 | First use go get to install the latest version of the library. | ||
52 | |||
53 | $ go get github.com/spf13/afero | ||
54 | |||
55 | Next include Afero in your application. | ||
56 | ```go | ||
57 | import "github.com/spf13/afero" | ||
58 | ``` | ||
59 | |||
60 | ## Step 2: Declare a backend | ||
61 | |||
62 | First define a package variable and set it to a pointer to a filesystem. | ||
63 | ```go | ||
64 | var AppFs = afero.NewMemMapFs() | ||
65 | |||
66 | or | ||
67 | |||
68 | var AppFs = afero.NewOsFs() | ||
69 | ``` | ||
70 | It is important to note that if you repeat the composite literal you | ||
71 | will be using a completely new and isolated filesystem. In the case of | ||
72 | OsFs it will still use the same underlying filesystem but will reduce | ||
73 | the ability to drop in other filesystems as desired. | ||
74 | |||
75 | ## Step 3: Use it like you would the OS package | ||
76 | |||
77 | Throughout your application use any function and method like you normally | ||
78 | would. | ||
79 | |||
80 | So if my application before had: | ||
81 | ```go | ||
82 | os.Open('/tmp/foo') | ||
83 | ``` | ||
84 | We would replace it with: | ||
85 | ```go | ||
86 | AppFs.Open('/tmp/foo') | ||
87 | ``` | ||
88 | |||
89 | `AppFs` being the variable we defined above. | ||
90 | |||
91 | |||
92 | ## List of all available functions | ||
93 | |||
94 | File System Methods Available: | ||
95 | ```go | ||
96 | Chmod(name string, mode os.FileMode) : error | ||
97 | Chtimes(name string, atime time.Time, mtime time.Time) : error | ||
98 | Create(name string) : File, error | ||
99 | Mkdir(name string, perm os.FileMode) : error | ||
100 | MkdirAll(path string, perm os.FileMode) : error | ||
101 | Name() : string | ||
102 | Open(name string) : File, error | ||
103 | OpenFile(name string, flag int, perm os.FileMode) : File, error | ||
104 | Remove(name string) : error | ||
105 | RemoveAll(path string) : error | ||
106 | Rename(oldname, newname string) : error | ||
107 | Stat(name string) : os.FileInfo, error | ||
108 | ``` | ||
109 | File Interfaces and Methods Available: | ||
110 | ```go | ||
111 | io.Closer | ||
112 | io.Reader | ||
113 | io.ReaderAt | ||
114 | io.Seeker | ||
115 | io.Writer | ||
116 | io.WriterAt | ||
117 | |||
118 | Name() : string | ||
119 | Readdir(count int) : []os.FileInfo, error | ||
120 | Readdirnames(n int) : []string, error | ||
121 | Stat() : os.FileInfo, error | ||
122 | Sync() : error | ||
123 | Truncate(size int64) : error | ||
124 | WriteString(s string) : ret int, err error | ||
125 | ``` | ||
126 | In some applications it may make sense to define a new package that | ||
127 | simply exports the file system variable for easy access from anywhere. | ||
128 | |||
129 | ## Using Afero's utility functions | ||
130 | |||
131 | Afero provides a set of functions to make it easier to use the underlying file systems. | ||
132 | These functions have been primarily ported from io & ioutil with some developed for Hugo. | ||
133 | |||
134 | The afero utilities support all afero compatible backends. | ||
135 | |||
136 | The list of utilities includes: | ||
137 | |||
138 | ```go | ||
139 | DirExists(path string) (bool, error) | ||
140 | Exists(path string) (bool, error) | ||
141 | FileContainsBytes(filename string, subslice []byte) (bool, error) | ||
142 | GetTempDir(subPath string) string | ||
143 | IsDir(path string) (bool, error) | ||
144 | IsEmpty(path string) (bool, error) | ||
145 | ReadDir(dirname string) ([]os.FileInfo, error) | ||
146 | ReadFile(filename string) ([]byte, error) | ||
147 | SafeWriteReader(path string, r io.Reader) (err error) | ||
148 | TempDir(dir, prefix string) (name string, err error) | ||
149 | TempFile(dir, prefix string) (f File, err error) | ||
150 | Walk(root string, walkFn filepath.WalkFunc) error | ||
151 | WriteFile(filename string, data []byte, perm os.FileMode) error | ||
152 | WriteReader(path string, r io.Reader) (err error) | ||
153 | ``` | ||
154 | For a complete list see [Afero's GoDoc](https://godoc.org/github.com/spf13/afero) | ||
155 | |||
156 | They are available under two different approaches to use. You can either call | ||
157 | them directly where the first parameter of each function will be the file | ||
158 | system, or you can declare a new `Afero`, a custom type used to bind these | ||
159 | functions as methods to a given filesystem. | ||
160 | |||
161 | ### Calling utilities directly | ||
162 | |||
163 | ```go | ||
164 | fs := new(afero.MemMapFs) | ||
165 | f, err := afero.TempFile(fs,"", "ioutil-test") | ||
166 | |||
167 | ``` | ||
168 | |||
169 | ### Calling via Afero | ||
170 | |||
171 | ```go | ||
172 | fs := afero.NewMemMapFs() | ||
173 | afs := &afero.Afero{Fs: fs} | ||
174 | f, err := afs.TempFile("", "ioutil-test") | ||
175 | ``` | ||
176 | |||
177 | ## Using Afero for Testing | ||
178 | |||
179 | There is a large benefit to using a mock filesystem for testing. It has a | ||
180 | completely blank state every time it is initialized and can be easily | ||
181 | reproducible regardless of OS. You could create files to your heart’s content | ||
182 | and the file access would be fast while also saving you from all the annoying | ||
183 | issues with deleting temporary files, Windows file locking, etc. The MemMapFs | ||
184 | backend is perfect for testing. | ||
185 | |||
186 | * Much faster than performing I/O operations on disk | ||
187 | * Avoid security issues and permissions | ||
188 | * Far more control. 'rm -rf /' with confidence | ||
189 | * Test setup is far more easier to do | ||
190 | * No test cleanup needed | ||
191 | |||
192 | One way to accomplish this is to define a variable as mentioned above. | ||
193 | In your application this will be set to afero.NewOsFs() during testing you | ||
194 | can set it to afero.NewMemMapFs(). | ||
195 | |||
196 | It wouldn't be uncommon to have each test initialize a blank slate memory | ||
197 | backend. To do this I would define my `appFS = afero.NewOsFs()` somewhere | ||
198 | appropriate in my application code. This approach ensures that Tests are order | ||
199 | independent, with no test relying on the state left by an earlier test. | ||
200 | |||
201 | Then in my tests I would initialize a new MemMapFs for each test: | ||
202 | ```go | ||
203 | func TestExist(t *testing.T) { | ||
204 | appFS := afero.NewMemMapFs() | ||
205 | // create test files and directories | ||
206 | appFS.MkdirAll("src/a", 0755) | ||
207 | afero.WriteFile(appFS, "src/a/b", []byte("file b"), 0644) | ||
208 | afero.WriteFile(appFS, "src/c", []byte("file c"), 0644) | ||
209 | name := "src/c" | ||
210 | _, err := appFS.Stat(name) | ||
211 | if os.IsNotExist(err) { | ||
212 | t.Errorf("file \"%s\" does not exist.\n", name) | ||
213 | } | ||
214 | } | ||
215 | ``` | ||
216 | |||
217 | # Available Backends | ||
218 | |||
219 | ## Operating System Native | ||
220 | |||
221 | ### OsFs | ||
222 | |||
223 | The first is simply a wrapper around the native OS calls. This makes it | ||
224 | very easy to use as all of the calls are the same as the existing OS | ||
225 | calls. It also makes it trivial to have your code use the OS during | ||
226 | operation and a mock filesystem during testing or as needed. | ||
227 | |||
228 | ```go | ||
229 | appfs := afero.NewOsFs() | ||
230 | appfs.MkdirAll("src/a", 0755)) | ||
231 | ``` | ||
232 | |||
233 | ## Memory Backed Storage | ||
234 | |||
235 | ### MemMapFs | ||
236 | |||
237 | Afero also provides a fully atomic memory backed filesystem perfect for use in | ||
238 | mocking and to speed up unnecessary disk io when persistence isn’t | ||
239 | necessary. It is fully concurrent and will work within go routines | ||
240 | safely. | ||
241 | |||
242 | ```go | ||
243 | mm := afero.NewMemMapFs() | ||
244 | mm.MkdirAll("src/a", 0755)) | ||
245 | ``` | ||
246 | |||
247 | #### InMemoryFile | ||
248 | |||
249 | As part of MemMapFs, Afero also provides an atomic, fully concurrent memory | ||
250 | backed file implementation. This can be used in other memory backed file | ||
251 | systems with ease. Plans are to add a radix tree memory stored file | ||
252 | system using InMemoryFile. | ||
253 | |||
254 | ## Network Interfaces | ||
255 | |||
256 | ### SftpFs | ||
257 | |||
258 | Afero has experimental support for secure file transfer protocol (sftp). Which can | ||
259 | be used to perform file operations over a encrypted channel. | ||
260 | |||
261 | ## Filtering Backends | ||
262 | |||
263 | ### BasePathFs | ||
264 | |||
265 | The BasePathFs restricts all operations to a given path within an Fs. | ||
266 | The given file name to the operations on this Fs will be prepended with | ||
267 | the base path before calling the source Fs. | ||
268 | |||
269 | ```go | ||
270 | bp := afero.NewBasePathFs(afero.NewOsFs(), "/base/path") | ||
271 | ``` | ||
272 | |||
273 | ### ReadOnlyFs | ||
274 | |||
275 | A thin wrapper around the source Fs providing a read only view. | ||
276 | |||
277 | ```go | ||
278 | fs := afero.NewReadOnlyFs(afero.NewOsFs()) | ||
279 | _, err := fs.Create("/file.txt") | ||
280 | // err = syscall.EPERM | ||
281 | ``` | ||
282 | |||
283 | # RegexpFs | ||
284 | |||
285 | A filtered view on file names, any file NOT matching | ||
286 | the passed regexp will be treated as non-existing. | ||
287 | Files not matching the regexp provided will not be created. | ||
288 | Directories are not filtered. | ||
289 | |||
290 | ```go | ||
291 | fs := afero.NewRegexpFs(afero.NewMemMapFs(), regexp.MustCompile(`\.txt$`)) | ||
292 | _, err := fs.Create("/file.html") | ||
293 | // err = syscall.ENOENT | ||
294 | ``` | ||
295 | |||
296 | ### HttpFs | ||
297 | |||
298 | Afero provides an http compatible backend which can wrap any of the existing | ||
299 | backends. | ||
300 | |||
301 | The Http package requires a slightly specific version of Open which | ||
302 | returns an http.File type. | ||
303 | |||
304 | Afero provides an httpFs file system which satisfies this requirement. | ||
305 | Any Afero FileSystem can be used as an httpFs. | ||
306 | |||
307 | ```go | ||
308 | httpFs := afero.NewHttpFs(<ExistingFS>) | ||
309 | fileserver := http.FileServer(httpFs.Dir(<PATH>))) | ||
310 | http.Handle("/", fileserver) | ||
311 | ``` | ||
312 | |||
313 | ## Composite Backends | ||
314 | |||
315 | Afero provides the ability have two filesystems (or more) act as a single | ||
316 | file system. | ||
317 | |||
318 | ### CacheOnReadFs | ||
319 | |||
320 | The CacheOnReadFs will lazily make copies of any accessed files from the base | ||
321 | layer into the overlay. Subsequent reads will be pulled from the overlay | ||
322 | directly permitting the request is within the cache duration of when it was | ||
323 | created in the overlay. | ||
324 | |||
325 | If the base filesystem is writeable, any changes to files will be | ||
326 | done first to the base, then to the overlay layer. Write calls to open file | ||
327 | handles like `Write()` or `Truncate()` to the overlay first. | ||
328 | |||
329 | To writing files to the overlay only, you can use the overlay Fs directly (not | ||
330 | via the union Fs). | ||
331 | |||
332 | Cache files in the layer for the given time.Duration, a cache duration of 0 | ||
333 | means "forever" meaning the file will not be re-requested from the base ever. | ||
334 | |||
335 | A read-only base will make the overlay also read-only but still copy files | ||
336 | from the base to the overlay when they're not present (or outdated) in the | ||
337 | caching layer. | ||
338 | |||
339 | ```go | ||
340 | base := afero.NewOsFs() | ||
341 | layer := afero.NewMemMapFs() | ||
342 | ufs := afero.NewCacheOnReadFs(base, layer, 100 * time.Second) | ||
343 | ``` | ||
344 | |||
345 | ### CopyOnWriteFs() | ||
346 | |||
347 | The CopyOnWriteFs is a read only base file system with a potentially | ||
348 | writeable layer on top. | ||
349 | |||
350 | Read operations will first look in the overlay and if not found there, will | ||
351 | serve the file from the base. | ||
352 | |||
353 | Changes to the file system will only be made in the overlay. | ||
354 | |||
355 | Any attempt to modify a file found only in the base will copy the file to the | ||
356 | overlay layer before modification (including opening a file with a writable | ||
357 | handle). | ||
358 | |||
359 | Removing and Renaming files present only in the base layer is not currently | ||
360 | permitted. If a file is present in the base layer and the overlay, only the | ||
361 | overlay will be removed/renamed. | ||
362 | |||
363 | ```go | ||
364 | base := afero.NewOsFs() | ||
365 | roBase := afero.NewReadOnlyFs(base) | ||
366 | ufs := afero.NewCopyOnWriteFs(roBase, afero.NewMemMapFs()) | ||
367 | |||
368 | fh, _ = ufs.Create("/home/test/file2.txt") | ||
369 | fh.WriteString("This is a test") | ||
370 | fh.Close() | ||
371 | ``` | ||
372 | |||
373 | In this example all write operations will only occur in memory (MemMapFs) | ||
374 | leaving the base filesystem (OsFs) untouched. | ||
375 | |||
376 | |||
377 | ## Desired/possible backends | ||
378 | |||
379 | The following is a short list of possible backends we hope someone will | ||
380 | implement: | ||
381 | |||
382 | * SSH | ||
383 | * ZIP | ||
384 | * TAR | ||
385 | * S3 | ||
386 | |||
387 | # About the project | ||
388 | |||
389 | ## What's in the name | ||
390 | |||
391 | Afero comes from the latin roots Ad-Facere. | ||
392 | |||
393 | **"Ad"** is a prefix meaning "to". | ||
394 | |||
395 | **"Facere"** is a form of the root "faciō" making "make or do". | ||
396 | |||
397 | The literal meaning of afero is "to make" or "to do" which seems very fitting | ||
398 | for a library that allows one to make files and directories and do things with them. | ||
399 | |||
400 | The English word that shares the same roots as Afero is "affair". Affair shares | ||
401 | the same concept but as a noun it means "something that is made or done" or "an | ||
402 | object of a particular type". | ||
403 | |||
404 | It's also nice that unlike some of my other libraries (hugo, cobra, viper) it | ||
405 | Googles very well. | ||
406 | |||
407 | ## Release Notes | ||
408 | |||
409 | * **0.10.0** 2015.12.10 | ||
410 | * Full compatibility with Windows | ||
411 | * Introduction of afero utilities | ||
412 | * Test suite rewritten to work cross platform | ||
413 | * Normalize paths for MemMapFs | ||
414 | * Adding Sync to the file interface | ||
415 | * **Breaking Change** Walk and ReadDir have changed parameter order | ||
416 | * Moving types used by MemMapFs to a subpackage | ||
417 | * General bugfixes and improvements | ||
418 | * **0.9.0** 2015.11.05 | ||
419 | * New Walk function similar to filepath.Walk | ||
420 | * MemMapFs.OpenFile handles O_CREATE, O_APPEND, O_TRUNC | ||
421 | * MemMapFs.Remove now really deletes the file | ||
422 | * InMemoryFile.Readdir and Readdirnames work correctly | ||
423 | * InMemoryFile functions lock it for concurrent access | ||
424 | * Test suite improvements | ||
425 | * **0.8.0** 2014.10.28 | ||
426 | * First public version | ||
427 | * Interfaces feel ready for people to build using | ||
428 | * Interfaces satisfy all known uses | ||
429 | * MemMapFs passes the majority of the OS test suite | ||
430 | * OsFs passes the majority of the OS test suite | ||
431 | |||
432 | ## Contributing | ||
433 | |||
434 | 1. Fork it | ||
435 | 2. Create your feature branch (`git checkout -b my-new-feature`) | ||
436 | 3. Commit your changes (`git commit -am 'Add some feature'`) | ||
437 | 4. Push to the branch (`git push origin my-new-feature`) | ||
438 | 5. Create new Pull Request | ||
439 | |||
440 | ## Contributors | ||
441 | |||
442 | Names in no particular order: | ||
443 | |||
444 | * [spf13](https://github.com/spf13) | ||
445 | * [jaqx0r](https://github.com/jaqx0r) | ||
446 | * [mbertschler](https://github.com/mbertschler) | ||
447 | * [xor-gate](https://github.com/xor-gate) | ||
448 | |||
449 | ## License | ||
450 | |||
451 | Afero is released under the Apache 2.0 license. See | ||
452 | [LICENSE.txt](https://github.com/spf13/afero/blob/master/LICENSE.txt) | ||
diff --git a/vendor/github.com/spf13/afero/afero.go b/vendor/github.com/spf13/afero/afero.go new file mode 100644 index 0000000..f5b5e12 --- /dev/null +++ b/vendor/github.com/spf13/afero/afero.go | |||
@@ -0,0 +1,108 @@ | |||
1 | // Copyright © 2014 Steve Francia <spf@spf13.com>. | ||
2 | // Copyright 2013 tsuru authors. All rights reserved. | ||
3 | // | ||
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 | // | ||
9 | // Unless required by applicable law or agreed to in writing, software | ||
10 | // distributed under the License is distributed on an "AS IS" BASIS, | ||
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
12 | // See the License for the specific language governing permissions and | ||
13 | // limitations under the License. | ||
14 | |||
15 | // Package afero provides types and methods for interacting with the filesystem, | ||
16 | // as an abstraction layer. | ||
17 | |||
18 | // Afero also provides a few implementations that are mostly interoperable. One that | ||
19 | // uses the operating system filesystem, one that uses memory to store files | ||
20 | // (cross platform) and an interface that should be implemented if you want to | ||
21 | // provide your own filesystem. | ||
22 | |||
23 | package afero | ||
24 | |||
25 | import ( | ||
26 | "errors" | ||
27 | "io" | ||
28 | "os" | ||
29 | "time" | ||
30 | ) | ||
31 | |||
32 | type Afero struct { | ||
33 | Fs | ||
34 | } | ||
35 | |||
36 | // File represents a file in the filesystem. | ||
37 | type File interface { | ||
38 | io.Closer | ||
39 | io.Reader | ||
40 | io.ReaderAt | ||
41 | io.Seeker | ||
42 | io.Writer | ||
43 | io.WriterAt | ||
44 | |||
45 | Name() string | ||
46 | Readdir(count int) ([]os.FileInfo, error) | ||
47 | Readdirnames(n int) ([]string, error) | ||
48 | Stat() (os.FileInfo, error) | ||
49 | Sync() error | ||
50 | Truncate(size int64) error | ||
51 | WriteString(s string) (ret int, err error) | ||
52 | } | ||
53 | |||
54 | // Fs is the filesystem interface. | ||
55 | // | ||
56 | // Any simulated or real filesystem should implement this interface. | ||
57 | type Fs interface { | ||
58 | // Create creates a file in the filesystem, returning the file and an | ||
59 | // error, if any happens. | ||
60 | Create(name string) (File, error) | ||
61 | |||
62 | // Mkdir creates a directory in the filesystem, return an error if any | ||
63 | // happens. | ||
64 | Mkdir(name string, perm os.FileMode) error | ||
65 | |||
66 | // MkdirAll creates a directory path and all parents that does not exist | ||
67 | // yet. | ||
68 | MkdirAll(path string, perm os.FileMode) error | ||
69 | |||
70 | // Open opens a file, returning it or an error, if any happens. | ||
71 | Open(name string) (File, error) | ||
72 | |||
73 | // OpenFile opens a file using the given flags and the given mode. | ||
74 | OpenFile(name string, flag int, perm os.FileMode) (File, error) | ||
75 | |||
76 | // Remove removes a file identified by name, returning an error, if any | ||
77 | // happens. | ||
78 | Remove(name string) error | ||
79 | |||
80 | // RemoveAll removes a directory path and any children it contains. It | ||
81 | // does not fail if the path does not exist (return nil). | ||
82 | RemoveAll(path string) error | ||
83 | |||
84 | // Rename renames a file. | ||
85 | Rename(oldname, newname string) error | ||
86 | |||
87 | // Stat returns a FileInfo describing the named file, or an error, if any | ||
88 | // happens. | ||
89 | Stat(name string) (os.FileInfo, error) | ||
90 | |||
91 | // The name of this FileSystem | ||
92 | Name() string | ||
93 | |||
94 | //Chmod changes the mode of the named file to mode. | ||
95 | Chmod(name string, mode os.FileMode) error | ||
96 | |||
97 | //Chtimes changes the access and modification times of the named file | ||
98 | Chtimes(name string, atime time.Time, mtime time.Time) error | ||
99 | } | ||
100 | |||
101 | var ( | ||
102 | ErrFileClosed = errors.New("File is closed") | ||
103 | ErrOutOfRange = errors.New("Out of range") | ||
104 | ErrTooLarge = errors.New("Too large") | ||
105 | ErrFileNotFound = os.ErrNotExist | ||
106 | ErrFileExists = os.ErrExist | ||
107 | ErrDestinationExists = os.ErrExist | ||
108 | ) | ||
diff --git a/vendor/github.com/spf13/afero/appveyor.yml b/vendor/github.com/spf13/afero/appveyor.yml new file mode 100644 index 0000000..a633ad5 --- /dev/null +++ b/vendor/github.com/spf13/afero/appveyor.yml | |||
@@ -0,0 +1,15 @@ | |||
1 | version: '{build}' | ||
2 | clone_folder: C:\gopath\src\github.com\spf13\afero | ||
3 | environment: | ||
4 | GOPATH: C:\gopath | ||
5 | build_script: | ||
6 | - cmd: >- | ||
7 | go version | ||
8 | |||
9 | go env | ||
10 | |||
11 | go get -v github.com/spf13/afero/... | ||
12 | |||
13 | go build github.com/spf13/afero | ||
14 | test_script: | ||
15 | - cmd: go test -race -v github.com/spf13/afero/... | ||
diff --git a/vendor/github.com/spf13/afero/basepath.go b/vendor/github.com/spf13/afero/basepath.go new file mode 100644 index 0000000..616ff8f --- /dev/null +++ b/vendor/github.com/spf13/afero/basepath.go | |||
@@ -0,0 +1,180 @@ | |||
1 | package afero | ||
2 | |||
3 | import ( | ||
4 | "os" | ||
5 | "path/filepath" | ||
6 | "runtime" | ||
7 | "strings" | ||
8 | "time" | ||
9 | ) | ||
10 | |||
11 | var _ Lstater = (*BasePathFs)(nil) | ||
12 | |||
13 | // The BasePathFs restricts all operations to a given path within an Fs. | ||
14 | // The given file name to the operations on this Fs will be prepended with | ||
15 | // the base path before calling the base Fs. | ||
16 | // Any file name (after filepath.Clean()) outside this base path will be | ||
17 | // treated as non existing file. | ||
18 | // | ||
19 | // Note that it does not clean the error messages on return, so you may | ||
20 | // reveal the real path on errors. | ||
21 | type BasePathFs struct { | ||
22 | source Fs | ||
23 | path string | ||
24 | } | ||
25 | |||
26 | type BasePathFile struct { | ||
27 | File | ||
28 | path string | ||
29 | } | ||
30 | |||
31 | func (f *BasePathFile) Name() string { | ||
32 | sourcename := f.File.Name() | ||
33 | return strings.TrimPrefix(sourcename, filepath.Clean(f.path)) | ||
34 | } | ||
35 | |||
36 | func NewBasePathFs(source Fs, path string) Fs { | ||
37 | return &BasePathFs{source: source, path: path} | ||
38 | } | ||
39 | |||
40 | // on a file outside the base path it returns the given file name and an error, | ||
41 | // else the given file with the base path prepended | ||
42 | func (b *BasePathFs) RealPath(name string) (path string, err error) { | ||
43 | if err := validateBasePathName(name); err != nil { | ||
44 | return name, err | ||
45 | } | ||
46 | |||
47 | bpath := filepath.Clean(b.path) | ||
48 | path = filepath.Clean(filepath.Join(bpath, name)) | ||
49 | if !strings.HasPrefix(path, bpath) { | ||
50 | return name, os.ErrNotExist | ||
51 | } | ||
52 | |||
53 | return path, nil | ||
54 | } | ||
55 | |||
56 | func validateBasePathName(name string) error { | ||
57 | if runtime.GOOS != "windows" { | ||
58 | // Not much to do here; | ||
59 | // the virtual file paths all look absolute on *nix. | ||
60 | return nil | ||
61 | } | ||
62 | |||
63 | // On Windows a common mistake would be to provide an absolute OS path | ||
64 | // We could strip out the base part, but that would not be very portable. | ||
65 | if filepath.IsAbs(name) { | ||
66 | return os.ErrNotExist | ||
67 | } | ||
68 | |||
69 | return nil | ||
70 | } | ||
71 | |||
72 | func (b *BasePathFs) Chtimes(name string, atime, mtime time.Time) (err error) { | ||
73 | if name, err = b.RealPath(name); err != nil { | ||
74 | return &os.PathError{Op: "chtimes", Path: name, Err: err} | ||
75 | } | ||
76 | return b.source.Chtimes(name, atime, mtime) | ||
77 | } | ||
78 | |||
79 | func (b *BasePathFs) Chmod(name string, mode os.FileMode) (err error) { | ||
80 | if name, err = b.RealPath(name); err != nil { | ||
81 | return &os.PathError{Op: "chmod", Path: name, Err: err} | ||
82 | } | ||
83 | return b.source.Chmod(name, mode) | ||
84 | } | ||
85 | |||
86 | func (b *BasePathFs) Name() string { | ||
87 | return "BasePathFs" | ||
88 | } | ||
89 | |||
90 | func (b *BasePathFs) Stat(name string) (fi os.FileInfo, err error) { | ||
91 | if name, err = b.RealPath(name); err != nil { | ||
92 | return nil, &os.PathError{Op: "stat", Path: name, Err: err} | ||
93 | } | ||
94 | return b.source.Stat(name) | ||
95 | } | ||
96 | |||
97 | func (b *BasePathFs) Rename(oldname, newname string) (err error) { | ||
98 | if oldname, err = b.RealPath(oldname); err != nil { | ||
99 | return &os.PathError{Op: "rename", Path: oldname, Err: err} | ||
100 | } | ||
101 | if newname, err = b.RealPath(newname); err != nil { | ||
102 | return &os.PathError{Op: "rename", Path: newname, Err: err} | ||
103 | } | ||
104 | return b.source.Rename(oldname, newname) | ||
105 | } | ||
106 | |||
107 | func (b *BasePathFs) RemoveAll(name string) (err error) { | ||
108 | if name, err = b.RealPath(name); err != nil { | ||
109 | return &os.PathError{Op: "remove_all", Path: name, Err: err} | ||
110 | } | ||
111 | return b.source.RemoveAll(name) | ||
112 | } | ||
113 | |||
114 | func (b *BasePathFs) Remove(name string) (err error) { | ||
115 | if name, err = b.RealPath(name); err != nil { | ||
116 | return &os.PathError{Op: "remove", Path: name, Err: err} | ||
117 | } | ||
118 | return b.source.Remove(name) | ||
119 | } | ||
120 | |||
121 | func (b *BasePathFs) OpenFile(name string, flag int, mode os.FileMode) (f File, err error) { | ||
122 | if name, err = b.RealPath(name); err != nil { | ||
123 | return nil, &os.PathError{Op: "openfile", Path: name, Err: err} | ||
124 | } | ||
125 | sourcef, err := b.source.OpenFile(name, flag, mode) | ||
126 | if err != nil { | ||
127 | return nil, err | ||
128 | } | ||
129 | return &BasePathFile{sourcef, b.path}, nil | ||
130 | } | ||
131 | |||
132 | func (b *BasePathFs) Open(name string) (f File, err error) { | ||
133 | if name, err = b.RealPath(name); err != nil { | ||
134 | return nil, &os.PathError{Op: "open", Path: name, Err: err} | ||
135 | } | ||
136 | sourcef, err := b.source.Open(name) | ||
137 | if err != nil { | ||
138 | return nil, err | ||
139 | } | ||
140 | return &BasePathFile{File: sourcef, path: b.path}, nil | ||
141 | } | ||
142 | |||
143 | func (b *BasePathFs) Mkdir(name string, mode os.FileMode) (err error) { | ||
144 | if name, err = b.RealPath(name); err != nil { | ||
145 | return &os.PathError{Op: "mkdir", Path: name, Err: err} | ||
146 | } | ||
147 | return b.source.Mkdir(name, mode) | ||
148 | } | ||
149 | |||
150 | func (b *BasePathFs) MkdirAll(name string, mode os.FileMode) (err error) { | ||
151 | if name, err = b.RealPath(name); err != nil { | ||
152 | return &os.PathError{Op: "mkdir", Path: name, Err: err} | ||
153 | } | ||
154 | return b.source.MkdirAll(name, mode) | ||
155 | } | ||
156 | |||
157 | func (b *BasePathFs) Create(name string) (f File, err error) { | ||
158 | if name, err = b.RealPath(name); err != nil { | ||
159 | return nil, &os.PathError{Op: "create", Path: name, Err: err} | ||
160 | } | ||
161 | sourcef, err := b.source.Create(name) | ||
162 | if err != nil { | ||
163 | return nil, err | ||
164 | } | ||
165 | return &BasePathFile{File: sourcef, path: b.path}, nil | ||
166 | } | ||
167 | |||
168 | func (b *BasePathFs) LstatIfPossible(name string) (os.FileInfo, bool, error) { | ||
169 | name, err := b.RealPath(name) | ||
170 | if err != nil { | ||
171 | return nil, false, &os.PathError{Op: "lstat", Path: name, Err: err} | ||
172 | } | ||
173 | if lstater, ok := b.source.(Lstater); ok { | ||
174 | return lstater.LstatIfPossible(name) | ||
175 | } | ||
176 | fi, err := b.source.Stat(name) | ||
177 | return fi, false, err | ||
178 | } | ||
179 | |||
180 | // vim: ts=4 sw=4 noexpandtab nolist syn=go | ||
diff --git a/vendor/github.com/spf13/afero/cacheOnReadFs.go b/vendor/github.com/spf13/afero/cacheOnReadFs.go new file mode 100644 index 0000000..29a26c6 --- /dev/null +++ b/vendor/github.com/spf13/afero/cacheOnReadFs.go | |||
@@ -0,0 +1,290 @@ | |||
1 | package afero | ||
2 | |||
3 | import ( | ||
4 | "os" | ||
5 | "syscall" | ||
6 | "time" | ||
7 | ) | ||
8 | |||
9 | // If the cache duration is 0, cache time will be unlimited, i.e. once | ||
10 | // a file is in the layer, the base will never be read again for this file. | ||
11 | // | ||
12 | // For cache times greater than 0, the modification time of a file is | ||
13 | // checked. Note that a lot of file system implementations only allow a | ||
14 | // resolution of a second for timestamps... or as the godoc for os.Chtimes() | ||
15 | // states: "The underlying filesystem may truncate or round the values to a | ||
16 | // less precise time unit." | ||
17 | // | ||
18 | // This caching union will forward all write calls also to the base file | ||
19 | // system first. To prevent writing to the base Fs, wrap it in a read-only | ||
20 | // filter - Note: this will also make the overlay read-only, for writing files | ||
21 | // in the overlay, use the overlay Fs directly, not via the union Fs. | ||
22 | type CacheOnReadFs struct { | ||
23 | base Fs | ||
24 | layer Fs | ||
25 | cacheTime time.Duration | ||
26 | } | ||
27 | |||
28 | func NewCacheOnReadFs(base Fs, layer Fs, cacheTime time.Duration) Fs { | ||
29 | return &CacheOnReadFs{base: base, layer: layer, cacheTime: cacheTime} | ||
30 | } | ||
31 | |||
32 | type cacheState int | ||
33 | |||
34 | const ( | ||
35 | // not present in the overlay, unknown if it exists in the base: | ||
36 | cacheMiss cacheState = iota | ||
37 | // present in the overlay and in base, base file is newer: | ||
38 | cacheStale | ||
39 | // present in the overlay - with cache time == 0 it may exist in the base, | ||
40 | // with cacheTime > 0 it exists in the base and is same age or newer in the | ||
41 | // overlay | ||
42 | cacheHit | ||
43 | // happens if someone writes directly to the overlay without | ||
44 | // going through this union | ||
45 | cacheLocal | ||
46 | ) | ||
47 | |||
48 | func (u *CacheOnReadFs) cacheStatus(name string) (state cacheState, fi os.FileInfo, err error) { | ||
49 | var lfi, bfi os.FileInfo | ||
50 | lfi, err = u.layer.Stat(name) | ||
51 | if err == nil { | ||
52 | if u.cacheTime == 0 { | ||
53 | return cacheHit, lfi, nil | ||
54 | } | ||
55 | if lfi.ModTime().Add(u.cacheTime).Before(time.Now()) { | ||
56 | bfi, err = u.base.Stat(name) | ||
57 | if err != nil { | ||
58 | return cacheLocal, lfi, nil | ||
59 | } | ||
60 | if bfi.ModTime().After(lfi.ModTime()) { | ||
61 | return cacheStale, bfi, nil | ||
62 | } | ||
63 | } | ||
64 | return cacheHit, lfi, nil | ||
65 | } | ||
66 | |||
67 | if err == syscall.ENOENT || os.IsNotExist(err) { | ||
68 | return cacheMiss, nil, nil | ||
69 | } | ||
70 | |||
71 | return cacheMiss, nil, err | ||
72 | } | ||
73 | |||
74 | func (u *CacheOnReadFs) copyToLayer(name string) error { | ||
75 | return copyToLayer(u.base, u.layer, name) | ||
76 | } | ||
77 | |||
78 | func (u *CacheOnReadFs) Chtimes(name string, atime, mtime time.Time) error { | ||
79 | st, _, err := u.cacheStatus(name) | ||
80 | if err != nil { | ||
81 | return err | ||
82 | } | ||
83 | switch st { | ||
84 | case cacheLocal: | ||
85 | case cacheHit: | ||
86 | err = u.base.Chtimes(name, atime, mtime) | ||
87 | case cacheStale, cacheMiss: | ||
88 | if err := u.copyToLayer(name); err != nil { | ||
89 | return err | ||
90 | } | ||
91 | err = u.base.Chtimes(name, atime, mtime) | ||
92 | } | ||
93 | if err != nil { | ||
94 | return err | ||
95 | } | ||
96 | return u.layer.Chtimes(name, atime, mtime) | ||
97 | } | ||
98 | |||
99 | func (u *CacheOnReadFs) Chmod(name string, mode os.FileMode) error { | ||
100 | st, _, err := u.cacheStatus(name) | ||
101 | if err != nil { | ||
102 | return err | ||
103 | } | ||
104 | switch st { | ||
105 | case cacheLocal: | ||
106 | case cacheHit: | ||
107 | err = u.base.Chmod(name, mode) | ||
108 | case cacheStale, cacheMiss: | ||
109 | if err := u.copyToLayer(name); err != nil { | ||
110 | return err | ||
111 | } | ||
112 | err = u.base.Chmod(name, mode) | ||
113 | } | ||
114 | if err != nil { | ||
115 | return err | ||
116 | } | ||
117 | return u.layer.Chmod(name, mode) | ||
118 | } | ||
119 | |||
120 | func (u *CacheOnReadFs) Stat(name string) (os.FileInfo, error) { | ||
121 | st, fi, err := u.cacheStatus(name) | ||
122 | if err != nil { | ||
123 | return nil, err | ||
124 | } | ||
125 | switch st { | ||
126 | case cacheMiss: | ||
127 | return u.base.Stat(name) | ||
128 | default: // cacheStale has base, cacheHit and cacheLocal the layer os.FileInfo | ||
129 | return fi, nil | ||
130 | } | ||
131 | } | ||
132 | |||
133 | func (u *CacheOnReadFs) Rename(oldname, newname string) error { | ||
134 | st, _, err := u.cacheStatus(oldname) | ||
135 | if err != nil { | ||
136 | return err | ||
137 | } | ||
138 | switch st { | ||
139 | case cacheLocal: | ||
140 | case cacheHit: | ||
141 | err = u.base.Rename(oldname, newname) | ||
142 | case cacheStale, cacheMiss: | ||
143 | if err := u.copyToLayer(oldname); err != nil { | ||
144 | return err | ||
145 | } | ||
146 | err = u.base.Rename(oldname, newname) | ||
147 | } | ||
148 | if err != nil { | ||
149 | return err | ||
150 | } | ||
151 | return u.layer.Rename(oldname, newname) | ||
152 | } | ||
153 | |||
154 | func (u *CacheOnReadFs) Remove(name string) error { | ||
155 | st, _, err := u.cacheStatus(name) | ||
156 | if err != nil { | ||
157 | return err | ||
158 | } | ||
159 | switch st { | ||
160 | case cacheLocal: | ||
161 | case cacheHit, cacheStale, cacheMiss: | ||
162 | err = u.base.Remove(name) | ||
163 | } | ||
164 | if err != nil { | ||
165 | return err | ||
166 | } | ||
167 | return u.layer.Remove(name) | ||
168 | } | ||
169 | |||
170 | func (u *CacheOnReadFs) RemoveAll(name string) error { | ||
171 | st, _, err := u.cacheStatus(name) | ||
172 | if err != nil { | ||
173 | return err | ||
174 | } | ||
175 | switch st { | ||
176 | case cacheLocal: | ||
177 | case cacheHit, cacheStale, cacheMiss: | ||
178 | err = u.base.RemoveAll(name) | ||
179 | } | ||
180 | if err != nil { | ||
181 | return err | ||
182 | } | ||
183 | return u.layer.RemoveAll(name) | ||
184 | } | ||
185 | |||
186 | func (u *CacheOnReadFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) { | ||
187 | st, _, err := u.cacheStatus(name) | ||
188 | if err != nil { | ||
189 | return nil, err | ||
190 | } | ||
191 | switch st { | ||
192 | case cacheLocal, cacheHit: | ||
193 | default: | ||
194 | if err := u.copyToLayer(name); err != nil { | ||
195 | return nil, err | ||
196 | } | ||
197 | } | ||
198 | if flag&(os.O_WRONLY|syscall.O_RDWR|os.O_APPEND|os.O_CREATE|os.O_TRUNC) != 0 { | ||
199 | bfi, err := u.base.OpenFile(name, flag, perm) | ||
200 | if err != nil { | ||
201 | return nil, err | ||
202 | } | ||
203 | lfi, err := u.layer.OpenFile(name, flag, perm) | ||
204 | if err != nil { | ||
205 | bfi.Close() // oops, what if O_TRUNC was set and file opening in the layer failed...? | ||
206 | return nil, err | ||
207 | } | ||
208 | return &UnionFile{Base: bfi, Layer: lfi}, nil | ||
209 | } | ||
210 | return u.layer.OpenFile(name, flag, perm) | ||
211 | } | ||
212 | |||
213 | func (u *CacheOnReadFs) Open(name string) (File, error) { | ||
214 | st, fi, err := u.cacheStatus(name) | ||
215 | if err != nil { | ||
216 | return nil, err | ||
217 | } | ||
218 | |||
219 | switch st { | ||
220 | case cacheLocal: | ||
221 | return u.layer.Open(name) | ||
222 | |||
223 | case cacheMiss: | ||
224 | bfi, err := u.base.Stat(name) | ||
225 | if err != nil { | ||
226 | return nil, err | ||
227 | } | ||
228 | if bfi.IsDir() { | ||
229 | return u.base.Open(name) | ||
230 | } | ||
231 | if err := u.copyToLayer(name); err != nil { | ||
232 | return nil, err | ||
233 | } | ||
234 | return u.layer.Open(name) | ||
235 | |||
236 | case cacheStale: | ||
237 | if !fi.IsDir() { | ||
238 | if err := u.copyToLayer(name); err != nil { | ||
239 | return nil, err | ||
240 | } | ||
241 | return u.layer.Open(name) | ||
242 | } | ||
243 | case cacheHit: | ||
244 | if !fi.IsDir() { | ||
245 | return u.layer.Open(name) | ||
246 | } | ||
247 | } | ||
248 | // the dirs from cacheHit, cacheStale fall down here: | ||
249 | bfile, _ := u.base.Open(name) | ||
250 | lfile, err := u.layer.Open(name) | ||
251 | if err != nil && bfile == nil { | ||
252 | return nil, err | ||
253 | } | ||
254 | return &UnionFile{Base: bfile, Layer: lfile}, nil | ||
255 | } | ||
256 | |||
257 | func (u *CacheOnReadFs) Mkdir(name string, perm os.FileMode) error { | ||
258 | err := u.base.Mkdir(name, perm) | ||
259 | if err != nil { | ||
260 | return err | ||
261 | } | ||
262 | return u.layer.MkdirAll(name, perm) // yes, MkdirAll... we cannot assume it exists in the cache | ||
263 | } | ||
264 | |||
265 | func (u *CacheOnReadFs) Name() string { | ||
266 | return "CacheOnReadFs" | ||
267 | } | ||
268 | |||
269 | func (u *CacheOnReadFs) MkdirAll(name string, perm os.FileMode) error { | ||
270 | err := u.base.MkdirAll(name, perm) | ||
271 | if err != nil { | ||
272 | return err | ||
273 | } | ||
274 | return u.layer.MkdirAll(name, perm) | ||
275 | } | ||
276 | |||
277 | func (u *CacheOnReadFs) Create(name string) (File, error) { | ||
278 | bfh, err := u.base.Create(name) | ||
279 | if err != nil { | ||
280 | return nil, err | ||
281 | } | ||
282 | lfh, err := u.layer.Create(name) | ||
283 | if err != nil { | ||
284 | // oops, see comment about OS_TRUNC above, should we remove? then we have to | ||
285 | // remember if the file did not exist before | ||
286 | bfh.Close() | ||
287 | return nil, err | ||
288 | } | ||
289 | return &UnionFile{Base: bfh, Layer: lfh}, nil | ||
290 | } | ||
diff --git a/vendor/github.com/spf13/afero/const_bsds.go b/vendor/github.com/spf13/afero/const_bsds.go new file mode 100644 index 0000000..5728243 --- /dev/null +++ b/vendor/github.com/spf13/afero/const_bsds.go | |||
@@ -0,0 +1,22 @@ | |||
1 | // Copyright © 2016 Steve Francia <spf@spf13.com>. | ||
2 | // | ||
3 | // Licensed under the Apache License, Version 2.0 (the "License"); | ||
4 | // you may not use this file except in compliance with the License. | ||
5 | // You may obtain a copy of the License at | ||
6 | // http://www.apache.org/licenses/LICENSE-2.0 | ||
7 | // | ||
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 | // +build darwin openbsd freebsd netbsd dragonfly | ||
15 | |||
16 | package afero | ||
17 | |||
18 | import ( | ||
19 | "syscall" | ||
20 | ) | ||
21 | |||
22 | const BADFD = syscall.EBADF | ||
diff --git a/vendor/github.com/spf13/afero/const_win_unix.go b/vendor/github.com/spf13/afero/const_win_unix.go new file mode 100644 index 0000000..968fc27 --- /dev/null +++ b/vendor/github.com/spf13/afero/const_win_unix.go | |||
@@ -0,0 +1,25 @@ | |||
1 | // Copyright © 2016 Steve Francia <spf@spf13.com>. | ||
2 | // | ||
3 | // Licensed under the Apache License, Version 2.0 (the "License"); | ||
4 | // you may not use this file except in compliance with the License. | ||
5 | // You may obtain a copy of the License at | ||
6 | // http://www.apache.org/licenses/LICENSE-2.0 | ||
7 | // | ||
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 | // +build !darwin | ||
14 | // +build !openbsd | ||
15 | // +build !freebsd | ||
16 | // +build !dragonfly | ||
17 | // +build !netbsd | ||
18 | |||
19 | package afero | ||
20 | |||
21 | import ( | ||
22 | "syscall" | ||
23 | ) | ||
24 | |||
25 | const BADFD = syscall.EBADFD | ||
diff --git a/vendor/github.com/spf13/afero/copyOnWriteFs.go b/vendor/github.com/spf13/afero/copyOnWriteFs.go new file mode 100644 index 0000000..e8108a8 --- /dev/null +++ b/vendor/github.com/spf13/afero/copyOnWriteFs.go | |||
@@ -0,0 +1,293 @@ | |||
1 | package afero | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "os" | ||
6 | "path/filepath" | ||
7 | "syscall" | ||
8 | "time" | ||
9 | ) | ||
10 | |||
11 | var _ Lstater = (*CopyOnWriteFs)(nil) | ||
12 | |||
13 | // The CopyOnWriteFs is a union filesystem: a read only base file system with | ||
14 | // a possibly writeable layer on top. Changes to the file system will only | ||
15 | // be made in the overlay: Changing an existing file in the base layer which | ||
16 | // is not present in the overlay will copy the file to the overlay ("changing" | ||
17 | // includes also calls to e.g. Chtimes() and Chmod()). | ||
18 | // | ||
19 | // Reading directories is currently only supported via Open(), not OpenFile(). | ||
20 | type CopyOnWriteFs struct { | ||
21 | base Fs | ||
22 | layer Fs | ||
23 | } | ||
24 | |||
25 | func NewCopyOnWriteFs(base Fs, layer Fs) Fs { | ||
26 | return &CopyOnWriteFs{base: base, layer: layer} | ||
27 | } | ||
28 | |||
29 | // Returns true if the file is not in the overlay | ||
30 | func (u *CopyOnWriteFs) isBaseFile(name string) (bool, error) { | ||
31 | if _, err := u.layer.Stat(name); err == nil { | ||
32 | return false, nil | ||
33 | } | ||
34 | _, err := u.base.Stat(name) | ||
35 | if err != nil { | ||
36 | if oerr, ok := err.(*os.PathError); ok { | ||
37 | if oerr.Err == os.ErrNotExist || oerr.Err == syscall.ENOENT || oerr.Err == syscall.ENOTDIR { | ||
38 | return false, nil | ||
39 | } | ||
40 | } | ||
41 | if err == syscall.ENOENT { | ||
42 | return false, nil | ||
43 | } | ||
44 | } | ||
45 | return true, err | ||
46 | } | ||
47 | |||
48 | func (u *CopyOnWriteFs) copyToLayer(name string) error { | ||
49 | return copyToLayer(u.base, u.layer, name) | ||
50 | } | ||
51 | |||
52 | func (u *CopyOnWriteFs) Chtimes(name string, atime, mtime time.Time) error { | ||
53 | b, err := u.isBaseFile(name) | ||
54 | if err != nil { | ||
55 | return err | ||
56 | } | ||
57 | if b { | ||
58 | if err := u.copyToLayer(name); err != nil { | ||
59 | return err | ||
60 | } | ||
61 | } | ||
62 | return u.layer.Chtimes(name, atime, mtime) | ||
63 | } | ||
64 | |||
65 | func (u *CopyOnWriteFs) Chmod(name string, mode os.FileMode) error { | ||
66 | b, err := u.isBaseFile(name) | ||
67 | if err != nil { | ||
68 | return err | ||
69 | } | ||
70 | if b { | ||
71 | if err := u.copyToLayer(name); err != nil { | ||
72 | return err | ||
73 | } | ||
74 | } | ||
75 | return u.layer.Chmod(name, mode) | ||
76 | } | ||
77 | |||
78 | func (u *CopyOnWriteFs) Stat(name string) (os.FileInfo, error) { | ||
79 | fi, err := u.layer.Stat(name) | ||
80 | if err != nil { | ||
81 | isNotExist := u.isNotExist(err) | ||
82 | if isNotExist { | ||
83 | return u.base.Stat(name) | ||
84 | } | ||
85 | return nil, err | ||
86 | } | ||
87 | return fi, nil | ||
88 | } | ||
89 | |||
90 | func (u *CopyOnWriteFs) LstatIfPossible(name string) (os.FileInfo, bool, error) { | ||
91 | llayer, ok1 := u.layer.(Lstater) | ||
92 | lbase, ok2 := u.base.(Lstater) | ||
93 | |||
94 | if ok1 { | ||
95 | fi, b, err := llayer.LstatIfPossible(name) | ||
96 | if err == nil { | ||
97 | return fi, b, nil | ||
98 | } | ||
99 | |||
100 | if !u.isNotExist(err) { | ||
101 | return nil, b, err | ||
102 | } | ||
103 | } | ||
104 | |||
105 | if ok2 { | ||
106 | fi, b, err := lbase.LstatIfPossible(name) | ||
107 | if err == nil { | ||
108 | return fi, b, nil | ||
109 | } | ||
110 | if !u.isNotExist(err) { | ||
111 | return nil, b, err | ||
112 | } | ||
113 | } | ||
114 | |||
115 | fi, err := u.Stat(name) | ||
116 | |||
117 | return fi, false, err | ||
118 | } | ||
119 | |||
120 | func (u *CopyOnWriteFs) isNotExist(err error) bool { | ||
121 | if e, ok := err.(*os.PathError); ok { | ||
122 | err = e.Err | ||
123 | } | ||
124 | if err == os.ErrNotExist || err == syscall.ENOENT || err == syscall.ENOTDIR { | ||
125 | return true | ||
126 | } | ||
127 | return false | ||
128 | } | ||
129 | |||
130 | // Renaming files present only in the base layer is not permitted | ||
131 | func (u *CopyOnWriteFs) Rename(oldname, newname string) error { | ||
132 | b, err := u.isBaseFile(oldname) | ||
133 | if err != nil { | ||
134 | return err | ||
135 | } | ||
136 | if b { | ||
137 | return syscall.EPERM | ||
138 | } | ||
139 | return u.layer.Rename(oldname, newname) | ||
140 | } | ||
141 | |||
142 | // Removing files present only in the base layer is not permitted. If | ||
143 | // a file is present in the base layer and the overlay, only the overlay | ||
144 | // will be removed. | ||
145 | func (u *CopyOnWriteFs) Remove(name string) error { | ||
146 | err := u.layer.Remove(name) | ||
147 | switch err { | ||
148 | case syscall.ENOENT: | ||
149 | _, err = u.base.Stat(name) | ||
150 | if err == nil { | ||
151 | return syscall.EPERM | ||
152 | } | ||
153 | return syscall.ENOENT | ||
154 | default: | ||
155 | return err | ||
156 | } | ||
157 | } | ||
158 | |||
159 | func (u *CopyOnWriteFs) RemoveAll(name string) error { | ||
160 | err := u.layer.RemoveAll(name) | ||
161 | switch err { | ||
162 | case syscall.ENOENT: | ||
163 | _, err = u.base.Stat(name) | ||
164 | if err == nil { | ||
165 | return syscall.EPERM | ||
166 | } | ||
167 | return syscall.ENOENT | ||
168 | default: | ||
169 | return err | ||
170 | } | ||
171 | } | ||
172 | |||
173 | func (u *CopyOnWriteFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) { | ||
174 | b, err := u.isBaseFile(name) | ||
175 | if err != nil { | ||
176 | return nil, err | ||
177 | } | ||
178 | |||
179 | if flag&(os.O_WRONLY|os.O_RDWR|os.O_APPEND|os.O_CREATE|os.O_TRUNC) != 0 { | ||
180 | if b { | ||
181 | if err = u.copyToLayer(name); err != nil { | ||
182 | return nil, err | ||
183 | } | ||
184 | return u.layer.OpenFile(name, flag, perm) | ||
185 | } | ||
186 | |||
187 | dir := filepath.Dir(name) | ||
188 | isaDir, err := IsDir(u.base, dir) | ||
189 | if err != nil && !os.IsNotExist(err) { | ||
190 | return nil, err | ||
191 | } | ||
192 | if isaDir { | ||
193 | if err = u.layer.MkdirAll(dir, 0777); err != nil { | ||
194 | return nil, err | ||
195 | } | ||
196 | return u.layer.OpenFile(name, flag, perm) | ||
197 | } | ||
198 | |||
199 | isaDir, err = IsDir(u.layer, dir) | ||
200 | if err != nil { | ||
201 | return nil, err | ||
202 | } | ||
203 | if isaDir { | ||
204 | return u.layer.OpenFile(name, flag, perm) | ||
205 | } | ||
206 | |||
207 | return nil, &os.PathError{Op: "open", Path: name, Err: syscall.ENOTDIR} // ...or os.ErrNotExist? | ||
208 | } | ||
209 | if b { | ||
210 | return u.base.OpenFile(name, flag, perm) | ||
211 | } | ||
212 | return u.layer.OpenFile(name, flag, perm) | ||
213 | } | ||
214 | |||
215 | // This function handles the 9 different possibilities caused | ||
216 | // by the union which are the intersection of the following... | ||
217 | // layer: doesn't exist, exists as a file, and exists as a directory | ||
218 | // base: doesn't exist, exists as a file, and exists as a directory | ||
219 | func (u *CopyOnWriteFs) Open(name string) (File, error) { | ||
220 | // Since the overlay overrides the base we check that first | ||
221 | b, err := u.isBaseFile(name) | ||
222 | if err != nil { | ||
223 | return nil, err | ||
224 | } | ||
225 | |||
226 | // If overlay doesn't exist, return the base (base state irrelevant) | ||
227 | if b { | ||
228 | return u.base.Open(name) | ||
229 | } | ||
230 | |||
231 | // If overlay is a file, return it (base state irrelevant) | ||
232 | dir, err := IsDir(u.layer, name) | ||
233 | if err != nil { | ||
234 | return nil, err | ||
235 | } | ||
236 | if !dir { | ||
237 | return u.layer.Open(name) | ||
238 | } | ||
239 | |||
240 | // Overlay is a directory, base state now matters. | ||
241 | // Base state has 3 states to check but 2 outcomes: | ||
242 | // A. It's a file or non-readable in the base (return just the overlay) | ||
243 | // B. It's an accessible directory in the base (return a UnionFile) | ||
244 | |||
245 | // If base is file or nonreadable, return overlay | ||
246 | dir, err = IsDir(u.base, name) | ||
247 | if !dir || err != nil { | ||
248 | return u.layer.Open(name) | ||
249 | } | ||
250 | |||
251 | // Both base & layer are directories | ||
252 | // Return union file (if opens are without error) | ||
253 | bfile, bErr := u.base.Open(name) | ||
254 | lfile, lErr := u.layer.Open(name) | ||
255 | |||
256 | // If either have errors at this point something is very wrong. Return nil and the errors | ||
257 | if bErr != nil || lErr != nil { | ||
258 | return nil, fmt.Errorf("BaseErr: %v\nOverlayErr: %v", bErr, lErr) | ||
259 | } | ||
260 | |||
261 | return &UnionFile{Base: bfile, Layer: lfile}, nil | ||
262 | } | ||
263 | |||
264 | func (u *CopyOnWriteFs) Mkdir(name string, perm os.FileMode) error { | ||
265 | dir, err := IsDir(u.base, name) | ||
266 | if err != nil { | ||
267 | return u.layer.MkdirAll(name, perm) | ||
268 | } | ||
269 | if dir { | ||
270 | return ErrFileExists | ||
271 | } | ||
272 | return u.layer.MkdirAll(name, perm) | ||
273 | } | ||
274 | |||
275 | func (u *CopyOnWriteFs) Name() string { | ||
276 | return "CopyOnWriteFs" | ||
277 | } | ||
278 | |||
279 | func (u *CopyOnWriteFs) MkdirAll(name string, perm os.FileMode) error { | ||
280 | dir, err := IsDir(u.base, name) | ||
281 | if err != nil { | ||
282 | return u.layer.MkdirAll(name, perm) | ||
283 | } | ||
284 | if dir { | ||
285 | // This is in line with how os.MkdirAll behaves. | ||
286 | return nil | ||
287 | } | ||
288 | return u.layer.MkdirAll(name, perm) | ||
289 | } | ||
290 | |||
291 | func (u *CopyOnWriteFs) Create(name string) (File, error) { | ||
292 | return u.OpenFile(name, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0666) | ||
293 | } | ||
diff --git a/vendor/github.com/spf13/afero/go.mod b/vendor/github.com/spf13/afero/go.mod new file mode 100644 index 0000000..0868550 --- /dev/null +++ b/vendor/github.com/spf13/afero/go.mod | |||
@@ -0,0 +1,3 @@ | |||
1 | module github.com/spf13/afero | ||
2 | |||
3 | require golang.org/x/text v0.3.0 | ||
diff --git a/vendor/github.com/spf13/afero/go.sum b/vendor/github.com/spf13/afero/go.sum new file mode 100644 index 0000000..6bad37b --- /dev/null +++ b/vendor/github.com/spf13/afero/go.sum | |||
@@ -0,0 +1,2 @@ | |||
1 | golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= | ||
2 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | ||
diff --git a/vendor/github.com/spf13/afero/httpFs.go b/vendor/github.com/spf13/afero/httpFs.go new file mode 100644 index 0000000..c421936 --- /dev/null +++ b/vendor/github.com/spf13/afero/httpFs.go | |||
@@ -0,0 +1,110 @@ | |||
1 | // Copyright © 2014 Steve Francia <spf@spf13.com>. | ||
2 | // | ||
3 | // Licensed under the Apache License, Version 2.0 (the "License"); | ||
4 | // you may not use this file except in compliance with the License. | ||
5 | // You may obtain a copy of the License at | ||
6 | // http://www.apache.org/licenses/LICENSE-2.0 | ||
7 | // | ||
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 afero | ||
15 | |||
16 | import ( | ||
17 | "errors" | ||
18 | "net/http" | ||
19 | "os" | ||
20 | "path" | ||
21 | "path/filepath" | ||
22 | "strings" | ||
23 | "time" | ||
24 | ) | ||
25 | |||
26 | type httpDir struct { | ||
27 | basePath string | ||
28 | fs HttpFs | ||
29 | } | ||
30 | |||
31 | func (d httpDir) Open(name string) (http.File, error) { | ||
32 | if filepath.Separator != '/' && strings.IndexRune(name, filepath.Separator) >= 0 || | ||
33 | strings.Contains(name, "\x00") { | ||
34 | return nil, errors.New("http: invalid character in file path") | ||
35 | } | ||
36 | dir := string(d.basePath) | ||
37 | if dir == "" { | ||
38 | dir = "." | ||
39 | } | ||
40 | |||
41 | f, err := d.fs.Open(filepath.Join(dir, filepath.FromSlash(path.Clean("/"+name)))) | ||
42 | if err != nil { | ||
43 | return nil, err | ||
44 | } | ||
45 | return f, nil | ||
46 | } | ||
47 | |||
48 | type HttpFs struct { | ||
49 | source Fs | ||
50 | } | ||
51 | |||
52 | func NewHttpFs(source Fs) *HttpFs { | ||
53 | return &HttpFs{source: source} | ||
54 | } | ||
55 | |||
56 | func (h HttpFs) Dir(s string) *httpDir { | ||
57 | return &httpDir{basePath: s, fs: h} | ||
58 | } | ||
59 | |||
60 | func (h HttpFs) Name() string { return "h HttpFs" } | ||
61 | |||
62 | func (h HttpFs) Create(name string) (File, error) { | ||
63 | return h.source.Create(name) | ||
64 | } | ||
65 | |||
66 | func (h HttpFs) Chmod(name string, mode os.FileMode) error { | ||
67 | return h.source.Chmod(name, mode) | ||
68 | } | ||
69 | |||
70 | func (h HttpFs) Chtimes(name string, atime time.Time, mtime time.Time) error { | ||
71 | return h.source.Chtimes(name, atime, mtime) | ||
72 | } | ||
73 | |||
74 | func (h HttpFs) Mkdir(name string, perm os.FileMode) error { | ||
75 | return h.source.Mkdir(name, perm) | ||
76 | } | ||
77 | |||
78 | func (h HttpFs) MkdirAll(path string, perm os.FileMode) error { | ||
79 | return h.source.MkdirAll(path, perm) | ||
80 | } | ||
81 | |||
82 | func (h HttpFs) Open(name string) (http.File, error) { | ||
83 | f, err := h.source.Open(name) | ||
84 | if err == nil { | ||
85 | if httpfile, ok := f.(http.File); ok { | ||
86 | return httpfile, nil | ||
87 | } | ||
88 | } | ||
89 | return nil, err | ||
90 | } | ||
91 | |||
92 | func (h HttpFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) { | ||
93 | return h.source.OpenFile(name, flag, perm) | ||
94 | } | ||
95 | |||
96 | func (h HttpFs) Remove(name string) error { | ||
97 | return h.source.Remove(name) | ||
98 | } | ||
99 | |||
100 | func (h HttpFs) RemoveAll(path string) error { | ||
101 | return h.source.RemoveAll(path) | ||
102 | } | ||
103 | |||
104 | func (h HttpFs) Rename(oldname, newname string) error { | ||
105 | return h.source.Rename(oldname, newname) | ||
106 | } | ||
107 | |||
108 | func (h HttpFs) Stat(name string) (os.FileInfo, error) { | ||
109 | return h.source.Stat(name) | ||
110 | } | ||
diff --git a/vendor/github.com/spf13/afero/ioutil.go b/vendor/github.com/spf13/afero/ioutil.go new file mode 100644 index 0000000..5c3a3d8 --- /dev/null +++ b/vendor/github.com/spf13/afero/ioutil.go | |||
@@ -0,0 +1,230 @@ | |||
1 | // Copyright ©2015 The Go Authors | ||
2 | // Copyright ©2015 Steve Francia <spf@spf13.com> | ||
3 | // | ||
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 | // | ||
8 | // http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | // | ||
10 | // Unless required by applicable law or agreed to in writing, software | ||
11 | // distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | // See the License for the specific language governing permissions and | ||
14 | // limitations under the License. | ||
15 | |||
16 | package afero | ||
17 | |||
18 | import ( | ||
19 | "bytes" | ||
20 | "io" | ||
21 | "os" | ||
22 | "path/filepath" | ||
23 | "sort" | ||
24 | "strconv" | ||
25 | "sync" | ||
26 | "time" | ||
27 | ) | ||
28 | |||
29 | // byName implements sort.Interface. | ||
30 | type byName []os.FileInfo | ||
31 | |||
32 | func (f byName) Len() int { return len(f) } | ||
33 | func (f byName) Less(i, j int) bool { return f[i].Name() < f[j].Name() } | ||
34 | func (f byName) Swap(i, j int) { f[i], f[j] = f[j], f[i] } | ||
35 | |||
36 | // ReadDir reads the directory named by dirname and returns | ||
37 | // a list of sorted directory entries. | ||
38 | func (a Afero) ReadDir(dirname string) ([]os.FileInfo, error) { | ||
39 | return ReadDir(a.Fs, dirname) | ||
40 | } | ||
41 | |||
42 | func ReadDir(fs Fs, dirname string) ([]os.FileInfo, error) { | ||
43 | f, err := fs.Open(dirname) | ||
44 | if err != nil { | ||
45 | return nil, err | ||
46 | } | ||
47 | list, err := f.Readdir(-1) | ||
48 | f.Close() | ||
49 | if err != nil { | ||
50 | return nil, err | ||
51 | } | ||
52 | sort.Sort(byName(list)) | ||
53 | return list, nil | ||
54 | } | ||
55 | |||
56 | // ReadFile reads the file named by filename and returns the contents. | ||
57 | // A successful call returns err == nil, not err == EOF. Because ReadFile | ||
58 | // reads the whole file, it does not treat an EOF from Read as an error | ||
59 | // to be reported. | ||
60 | func (a Afero) ReadFile(filename string) ([]byte, error) { | ||
61 | return ReadFile(a.Fs, filename) | ||
62 | } | ||
63 | |||
64 | func ReadFile(fs Fs, filename string) ([]byte, error) { | ||
65 | f, err := fs.Open(filename) | ||
66 | if err != nil { | ||
67 | return nil, err | ||
68 | } | ||
69 | defer f.Close() | ||
70 | // It's a good but not certain bet that FileInfo will tell us exactly how much to | ||
71 | // read, so let's try it but be prepared for the answer to be wrong. | ||
72 | var n int64 | ||
73 | |||
74 | if fi, err := f.Stat(); err == nil { | ||
75 | // Don't preallocate a huge buffer, just in case. | ||
76 | if size := fi.Size(); size < 1e9 { | ||
77 | n = size | ||
78 | } | ||
79 | } | ||
80 | // As initial capacity for readAll, use n + a little extra in case Size is zero, | ||
81 | // and to avoid another allocation after Read has filled the buffer. The readAll | ||
82 | // call will read into its allocated internal buffer cheaply. If the size was | ||
83 | // wrong, we'll either waste some space off the end or reallocate as needed, but | ||
84 | // in the overwhelmingly common case we'll get it just right. | ||
85 | return readAll(f, n+bytes.MinRead) | ||
86 | } | ||
87 | |||
88 | // readAll reads from r until an error or EOF and returns the data it read | ||
89 | // from the internal buffer allocated with a specified capacity. | ||
90 | func readAll(r io.Reader, capacity int64) (b []byte, err error) { | ||
91 | buf := bytes.NewBuffer(make([]byte, 0, capacity)) | ||
92 | // If the buffer overflows, we will get bytes.ErrTooLarge. | ||
93 | // Return that as an error. Any other panic remains. | ||
94 | defer func() { | ||
95 | e := recover() | ||
96 | if e == nil { | ||
97 | return | ||
98 | } | ||
99 | if panicErr, ok := e.(error); ok && panicErr == bytes.ErrTooLarge { | ||
100 | err = panicErr | ||
101 | } else { | ||
102 | panic(e) | ||
103 | } | ||
104 | }() | ||
105 | _, err = buf.ReadFrom(r) | ||
106 | return buf.Bytes(), err | ||
107 | } | ||
108 | |||
109 | // ReadAll reads from r until an error or EOF and returns the data it read. | ||
110 | // A successful call returns err == nil, not err == EOF. Because ReadAll is | ||
111 | // defined to read from src until EOF, it does not treat an EOF from Read | ||
112 | // as an error to be reported. | ||
113 | func ReadAll(r io.Reader) ([]byte, error) { | ||
114 | return readAll(r, bytes.MinRead) | ||
115 | } | ||
116 | |||
117 | // WriteFile writes data to a file named by filename. | ||
118 | // If the file does not exist, WriteFile creates it with permissions perm; | ||
119 | // otherwise WriteFile truncates it before writing. | ||
120 | func (a Afero) WriteFile(filename string, data []byte, perm os.FileMode) error { | ||
121 | return WriteFile(a.Fs, filename, data, perm) | ||
122 | } | ||
123 | |||
124 | func WriteFile(fs Fs, filename string, data []byte, perm os.FileMode) error { | ||
125 | f, err := fs.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm) | ||
126 | if err != nil { | ||
127 | return err | ||
128 | } | ||
129 | n, err := f.Write(data) | ||
130 | if err == nil && n < len(data) { | ||
131 | err = io.ErrShortWrite | ||
132 | } | ||
133 | if err1 := f.Close(); err == nil { | ||
134 | err = err1 | ||
135 | } | ||
136 | return err | ||
137 | } | ||
138 | |||
139 | // Random number state. | ||
140 | // We generate random temporary file names so that there's a good | ||
141 | // chance the file doesn't exist yet - keeps the number of tries in | ||
142 | // TempFile to a minimum. | ||
143 | var rand uint32 | ||
144 | var randmu sync.Mutex | ||
145 | |||
146 | func reseed() uint32 { | ||
147 | return uint32(time.Now().UnixNano() + int64(os.Getpid())) | ||
148 | } | ||
149 | |||
150 | func nextSuffix() string { | ||
151 | randmu.Lock() | ||
152 | r := rand | ||
153 | if r == 0 { | ||
154 | r = reseed() | ||
155 | } | ||
156 | r = r*1664525 + 1013904223 // constants from Numerical Recipes | ||
157 | rand = r | ||
158 | randmu.Unlock() | ||
159 | return strconv.Itoa(int(1e9 + r%1e9))[1:] | ||
160 | } | ||
161 | |||
162 | // TempFile creates a new temporary file in the directory dir | ||
163 | // with a name beginning with prefix, opens the file for reading | ||
164 | // and writing, and returns the resulting *File. | ||
165 | // If dir is the empty string, TempFile uses the default directory | ||
166 | // for temporary files (see os.TempDir). | ||
167 | // Multiple programs calling TempFile simultaneously | ||
168 | // will not choose the same file. The caller can use f.Name() | ||
169 | // to find the pathname of the file. It is the caller's responsibility | ||
170 | // to remove the file when no longer needed. | ||
171 | func (a Afero) TempFile(dir, prefix string) (f File, err error) { | ||
172 | return TempFile(a.Fs, dir, prefix) | ||
173 | } | ||
174 | |||
175 | func TempFile(fs Fs, dir, prefix string) (f File, err error) { | ||
176 | if dir == "" { | ||
177 | dir = os.TempDir() | ||
178 | } | ||
179 | |||
180 | nconflict := 0 | ||
181 | for i := 0; i < 10000; i++ { | ||
182 | name := filepath.Join(dir, prefix+nextSuffix()) | ||
183 | f, err = fs.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600) | ||
184 | if os.IsExist(err) { | ||
185 | if nconflict++; nconflict > 10 { | ||
186 | randmu.Lock() | ||
187 | rand = reseed() | ||
188 | randmu.Unlock() | ||
189 | } | ||
190 | continue | ||
191 | } | ||
192 | break | ||
193 | } | ||
194 | return | ||
195 | } | ||
196 | |||
197 | // TempDir creates a new temporary directory in the directory dir | ||
198 | // with a name beginning with prefix and returns the path of the | ||
199 | // new directory. If dir is the empty string, TempDir uses the | ||
200 | // default directory for temporary files (see os.TempDir). | ||
201 | // Multiple programs calling TempDir simultaneously | ||
202 | // will not choose the same directory. It is the caller's responsibility | ||
203 | // to remove the directory when no longer needed. | ||
204 | func (a Afero) TempDir(dir, prefix string) (name string, err error) { | ||
205 | return TempDir(a.Fs, dir, prefix) | ||
206 | } | ||
207 | func TempDir(fs Fs, dir, prefix string) (name string, err error) { | ||
208 | if dir == "" { | ||
209 | dir = os.TempDir() | ||
210 | } | ||
211 | |||
212 | nconflict := 0 | ||
213 | for i := 0; i < 10000; i++ { | ||
214 | try := filepath.Join(dir, prefix+nextSuffix()) | ||
215 | err = fs.Mkdir(try, 0700) | ||
216 | if os.IsExist(err) { | ||
217 | if nconflict++; nconflict > 10 { | ||
218 | randmu.Lock() | ||
219 | rand = reseed() | ||
220 | randmu.Unlock() | ||
221 | } | ||
222 | continue | ||
223 | } | ||
224 | if err == nil { | ||
225 | name = try | ||
226 | } | ||
227 | break | ||
228 | } | ||
229 | return | ||
230 | } | ||
diff --git a/vendor/github.com/spf13/afero/lstater.go b/vendor/github.com/spf13/afero/lstater.go new file mode 100644 index 0000000..89c1bfc --- /dev/null +++ b/vendor/github.com/spf13/afero/lstater.go | |||
@@ -0,0 +1,27 @@ | |||
1 | // Copyright © 2018 Steve Francia <spf@spf13.com>. | ||
2 | // | ||
3 | // Licensed under the Apache License, Version 2.0 (the "License"); | ||
4 | // you may not use this file except in compliance with the License. | ||
5 | // You may obtain a copy of the License at | ||
6 | // http://www.apache.org/licenses/LICENSE-2.0 | ||
7 | // | ||
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 afero | ||
15 | |||
16 | import ( | ||
17 | "os" | ||
18 | ) | ||
19 | |||
20 | // Lstater is an optional interface in Afero. It is only implemented by the | ||
21 | // filesystems saying so. | ||
22 | // It will call Lstat if the filesystem iself is, or it delegates to, the os filesystem. | ||
23 | // Else it will call Stat. | ||
24 | // In addtion to the FileInfo, it will return a boolean telling whether Lstat was called or not. | ||
25 | type Lstater interface { | ||
26 | LstatIfPossible(name string) (os.FileInfo, bool, error) | ||
27 | } | ||
diff --git a/vendor/github.com/spf13/afero/match.go b/vendor/github.com/spf13/afero/match.go new file mode 100644 index 0000000..c18a87f --- /dev/null +++ b/vendor/github.com/spf13/afero/match.go | |||
@@ -0,0 +1,110 @@ | |||
1 | // Copyright © 2014 Steve Francia <spf@spf13.com>. | ||
2 | // Copyright 2009 The Go Authors. All rights reserved. | ||
3 | |||
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 | // | ||
9 | // Unless required by applicable law or agreed to in writing, software | ||
10 | // distributed under the License is distributed on an "AS IS" BASIS, | ||
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
12 | // See the License for the specific language governing permissions and | ||
13 | // limitations under the License. | ||
14 | |||
15 | package afero | ||
16 | |||
17 | import ( | ||
18 | "path/filepath" | ||
19 | "sort" | ||
20 | "strings" | ||
21 | ) | ||
22 | |||
23 | // Glob returns the names of all files matching pattern or nil | ||
24 | // if there is no matching file. The syntax of patterns is the same | ||
25 | // as in Match. The pattern may describe hierarchical names such as | ||
26 | // /usr/*/bin/ed (assuming the Separator is '/'). | ||
27 | // | ||
28 | // Glob ignores file system errors such as I/O errors reading directories. | ||
29 | // The only possible returned error is ErrBadPattern, when pattern | ||
30 | // is malformed. | ||
31 | // | ||
32 | // This was adapted from (http://golang.org/pkg/path/filepath) and uses several | ||
33 | // built-ins from that package. | ||
34 | func Glob(fs Fs, pattern string) (matches []string, err error) { | ||
35 | if !hasMeta(pattern) { | ||
36 | // Lstat not supported by a ll filesystems. | ||
37 | if _, err = lstatIfPossible(fs, pattern); err != nil { | ||
38 | return nil, nil | ||
39 | } | ||
40 | return []string{pattern}, nil | ||
41 | } | ||
42 | |||
43 | dir, file := filepath.Split(pattern) | ||
44 | switch dir { | ||
45 | case "": | ||
46 | dir = "." | ||
47 | case string(filepath.Separator): | ||
48 | // nothing | ||
49 | default: | ||
50 | dir = dir[0 : len(dir)-1] // chop off trailing separator | ||
51 | } | ||
52 | |||
53 | if !hasMeta(dir) { | ||
54 | return glob(fs, dir, file, nil) | ||
55 | } | ||
56 | |||
57 | var m []string | ||
58 | m, err = Glob(fs, dir) | ||
59 | if err != nil { | ||
60 | return | ||
61 | } | ||
62 | for _, d := range m { | ||
63 | matches, err = glob(fs, d, file, matches) | ||
64 | if err != nil { | ||
65 | return | ||
66 | } | ||
67 | } | ||
68 | return | ||
69 | } | ||
70 | |||
71 | // glob searches for files matching pattern in the directory dir | ||
72 | // and appends them to matches. If the directory cannot be | ||
73 | // opened, it returns the existing matches. New matches are | ||
74 | // added in lexicographical order. | ||
75 | func glob(fs Fs, dir, pattern string, matches []string) (m []string, e error) { | ||
76 | m = matches | ||
77 | fi, err := fs.Stat(dir) | ||
78 | if err != nil { | ||
79 | return | ||
80 | } | ||
81 | if !fi.IsDir() { | ||
82 | return | ||
83 | } | ||
84 | d, err := fs.Open(dir) | ||
85 | if err != nil { | ||
86 | return | ||
87 | } | ||
88 | defer d.Close() | ||
89 | |||
90 | names, _ := d.Readdirnames(-1) | ||
91 | sort.Strings(names) | ||
92 | |||
93 | for _, n := range names { | ||
94 | matched, err := filepath.Match(pattern, n) | ||
95 | if err != nil { | ||
96 | return m, err | ||
97 | } | ||
98 | if matched { | ||
99 | m = append(m, filepath.Join(dir, n)) | ||
100 | } | ||
101 | } | ||
102 | return | ||
103 | } | ||
104 | |||
105 | // hasMeta reports whether path contains any of the magic characters | ||
106 | // recognized by Match. | ||
107 | func hasMeta(path string) bool { | ||
108 | // TODO(niemeyer): Should other magic characters be added here? | ||
109 | return strings.IndexAny(path, "*?[") >= 0 | ||
110 | } | ||
diff --git a/vendor/github.com/spf13/afero/mem/dir.go b/vendor/github.com/spf13/afero/mem/dir.go new file mode 100644 index 0000000..e104013 --- /dev/null +++ b/vendor/github.com/spf13/afero/mem/dir.go | |||
@@ -0,0 +1,37 @@ | |||
1 | // Copyright © 2014 Steve Francia <spf@spf13.com>. | ||
2 | // | ||
3 | // Licensed under the Apache License, Version 2.0 (the "License"); | ||
4 | // you may not use this file except in compliance with the License. | ||
5 | // You may obtain a copy of the License at | ||
6 | // http://www.apache.org/licenses/LICENSE-2.0 | ||
7 | // | ||
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 mem | ||
15 | |||
16 | type Dir interface { | ||
17 | Len() int | ||
18 | Names() []string | ||
19 | Files() []*FileData | ||
20 | Add(*FileData) | ||
21 | Remove(*FileData) | ||
22 | } | ||
23 | |||
24 | func RemoveFromMemDir(dir *FileData, f *FileData) { | ||
25 | dir.memDir.Remove(f) | ||
26 | } | ||
27 | |||
28 | func AddToMemDir(dir *FileData, f *FileData) { | ||
29 | dir.memDir.Add(f) | ||
30 | } | ||
31 | |||
32 | func InitializeDir(d *FileData) { | ||
33 | if d.memDir == nil { | ||
34 | d.dir = true | ||
35 | d.memDir = &DirMap{} | ||
36 | } | ||
37 | } | ||
diff --git a/vendor/github.com/spf13/afero/mem/dirmap.go b/vendor/github.com/spf13/afero/mem/dirmap.go new file mode 100644 index 0000000..03a57ee --- /dev/null +++ b/vendor/github.com/spf13/afero/mem/dirmap.go | |||
@@ -0,0 +1,43 @@ | |||
1 | // Copyright © 2015 Steve Francia <spf@spf13.com>. | ||
2 | // | ||
3 | // Licensed under the Apache License, Version 2.0 (the "License"); | ||
4 | // you may not use this file except in compliance with the License. | ||
5 | // You may obtain a copy of the License at | ||
6 | // http://www.apache.org/licenses/LICENSE-2.0 | ||
7 | // | ||
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 mem | ||
15 | |||
16 | import "sort" | ||
17 | |||
18 | type DirMap map[string]*FileData | ||
19 | |||
20 | func (m DirMap) Len() int { return len(m) } | ||
21 | func (m DirMap) Add(f *FileData) { m[f.name] = f } | ||
22 | func (m DirMap) Remove(f *FileData) { delete(m, f.name) } | ||
23 | func (m DirMap) Files() (files []*FileData) { | ||
24 | for _, f := range m { | ||
25 | files = append(files, f) | ||
26 | } | ||
27 | sort.Sort(filesSorter(files)) | ||
28 | return files | ||
29 | } | ||
30 | |||
31 | // implement sort.Interface for []*FileData | ||
32 | type filesSorter []*FileData | ||
33 | |||
34 | func (s filesSorter) Len() int { return len(s) } | ||
35 | func (s filesSorter) Swap(i, j int) { s[i], s[j] = s[j], s[i] } | ||
36 | func (s filesSorter) Less(i, j int) bool { return s[i].name < s[j].name } | ||
37 | |||
38 | func (m DirMap) Names() (names []string) { | ||
39 | for x := range m { | ||
40 | names = append(names, x) | ||
41 | } | ||
42 | return names | ||
43 | } | ||
diff --git a/vendor/github.com/spf13/afero/mem/file.go b/vendor/github.com/spf13/afero/mem/file.go new file mode 100644 index 0000000..7af2fb5 --- /dev/null +++ b/vendor/github.com/spf13/afero/mem/file.go | |||
@@ -0,0 +1,317 @@ | |||
1 | // Copyright © 2015 Steve Francia <spf@spf13.com>. | ||
2 | // Copyright 2013 tsuru authors. All rights reserved. | ||
3 | // | ||
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 | // | ||
9 | // Unless required by applicable law or agreed to in writing, software | ||
10 | // distributed under the License is distributed on an "AS IS" BASIS, | ||
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
12 | // See the License for the specific language governing permissions and | ||
13 | // limitations under the License. | ||
14 | |||
15 | package mem | ||
16 | |||
17 | import ( | ||
18 | "bytes" | ||
19 | "errors" | ||
20 | "io" | ||
21 | "os" | ||
22 | "path/filepath" | ||
23 | "sync" | ||
24 | "sync/atomic" | ||
25 | ) | ||
26 | |||
27 | import "time" | ||
28 | |||
29 | const FilePathSeparator = string(filepath.Separator) | ||
30 | |||
31 | type File struct { | ||
32 | // atomic requires 64-bit alignment for struct field access | ||
33 | at int64 | ||
34 | readDirCount int64 | ||
35 | closed bool | ||
36 | readOnly bool | ||
37 | fileData *FileData | ||
38 | } | ||
39 | |||
40 | func NewFileHandle(data *FileData) *File { | ||
41 | return &File{fileData: data} | ||
42 | } | ||
43 | |||
44 | func NewReadOnlyFileHandle(data *FileData) *File { | ||
45 | return &File{fileData: data, readOnly: true} | ||
46 | } | ||
47 | |||
48 | func (f File) Data() *FileData { | ||
49 | return f.fileData | ||
50 | } | ||
51 | |||
52 | type FileData struct { | ||
53 | sync.Mutex | ||
54 | name string | ||
55 | data []byte | ||
56 | memDir Dir | ||
57 | dir bool | ||
58 | mode os.FileMode | ||
59 | modtime time.Time | ||
60 | } | ||
61 | |||
62 | func (d *FileData) Name() string { | ||
63 | d.Lock() | ||
64 | defer d.Unlock() | ||
65 | return d.name | ||
66 | } | ||
67 | |||
68 | func CreateFile(name string) *FileData { | ||
69 | return &FileData{name: name, mode: os.ModeTemporary, modtime: time.Now()} | ||
70 | } | ||
71 | |||
72 | func CreateDir(name string) *FileData { | ||
73 | return &FileData{name: name, memDir: &DirMap{}, dir: true} | ||
74 | } | ||
75 | |||
76 | func ChangeFileName(f *FileData, newname string) { | ||
77 | f.Lock() | ||
78 | f.name = newname | ||
79 | f.Unlock() | ||
80 | } | ||
81 | |||
82 | func SetMode(f *FileData, mode os.FileMode) { | ||
83 | f.Lock() | ||
84 | f.mode = mode | ||
85 | f.Unlock() | ||
86 | } | ||
87 | |||
88 | func SetModTime(f *FileData, mtime time.Time) { | ||
89 | f.Lock() | ||
90 | setModTime(f, mtime) | ||
91 | f.Unlock() | ||
92 | } | ||
93 | |||
94 | func setModTime(f *FileData, mtime time.Time) { | ||
95 | f.modtime = mtime | ||
96 | } | ||
97 | |||
98 | func GetFileInfo(f *FileData) *FileInfo { | ||
99 | return &FileInfo{f} | ||
100 | } | ||
101 | |||
102 | func (f *File) Open() error { | ||
103 | atomic.StoreInt64(&f.at, 0) | ||
104 | atomic.StoreInt64(&f.readDirCount, 0) | ||
105 | f.fileData.Lock() | ||
106 | f.closed = false | ||
107 | f.fileData.Unlock() | ||
108 | return nil | ||
109 | } | ||
110 | |||
111 | func (f *File) Close() error { | ||
112 | f.fileData.Lock() | ||
113 | f.closed = true | ||
114 | if !f.readOnly { | ||
115 | setModTime(f.fileData, time.Now()) | ||
116 | } | ||
117 | f.fileData.Unlock() | ||
118 | return nil | ||
119 | } | ||
120 | |||
121 | func (f *File) Name() string { | ||
122 | return f.fileData.Name() | ||
123 | } | ||
124 | |||
125 | func (f *File) Stat() (os.FileInfo, error) { | ||
126 | return &FileInfo{f.fileData}, nil | ||
127 | } | ||
128 | |||
129 | func (f *File) Sync() error { | ||
130 | return nil | ||
131 | } | ||
132 | |||
133 | func (f *File) Readdir(count int) (res []os.FileInfo, err error) { | ||
134 | if !f.fileData.dir { | ||
135 | return nil, &os.PathError{Op: "readdir", Path: f.fileData.name, Err: errors.New("not a dir")} | ||
136 | } | ||
137 | var outLength int64 | ||
138 | |||
139 | f.fileData.Lock() | ||
140 | files := f.fileData.memDir.Files()[f.readDirCount:] | ||
141 | if count > 0 { | ||
142 | if len(files) < count { | ||
143 | outLength = int64(len(files)) | ||
144 | } else { | ||
145 | outLength = int64(count) | ||
146 | } | ||
147 | if len(files) == 0 { | ||
148 | err = io.EOF | ||
149 | } | ||
150 | } else { | ||
151 | outLength = int64(len(files)) | ||
152 | } | ||
153 | f.readDirCount += outLength | ||
154 | f.fileData.Unlock() | ||
155 | |||
156 | res = make([]os.FileInfo, outLength) | ||
157 | for i := range res { | ||
158 | res[i] = &FileInfo{files[i]} | ||
159 | } | ||
160 | |||
161 | return res, err | ||
162 | } | ||
163 | |||
164 | func (f *File) Readdirnames(n int) (names []string, err error) { | ||
165 | fi, err := f.Readdir(n) | ||
166 | names = make([]string, len(fi)) | ||
167 | for i, f := range fi { | ||
168 | _, names[i] = filepath.Split(f.Name()) | ||
169 | } | ||
170 | return names, err | ||
171 | } | ||
172 | |||
173 | func (f *File) Read(b []byte) (n int, err error) { | ||
174 | f.fileData.Lock() | ||
175 | defer f.fileData.Unlock() | ||
176 | if f.closed == true { | ||
177 | return 0, ErrFileClosed | ||
178 | } | ||
179 | if len(b) > 0 && int(f.at) == len(f.fileData.data) { | ||
180 | return 0, io.EOF | ||
181 | } | ||
182 | if int(f.at) > len(f.fileData.data) { | ||
183 | return 0, io.ErrUnexpectedEOF | ||
184 | } | ||
185 | if len(f.fileData.data)-int(f.at) >= len(b) { | ||
186 | n = len(b) | ||
187 | } else { | ||
188 | n = len(f.fileData.data) - int(f.at) | ||
189 | } | ||
190 | copy(b, f.fileData.data[f.at:f.at+int64(n)]) | ||
191 | atomic.AddInt64(&f.at, int64(n)) | ||
192 | return | ||
193 | } | ||
194 | |||
195 | func (f *File) ReadAt(b []byte, off int64) (n int, err error) { | ||
196 | atomic.StoreInt64(&f.at, off) | ||
197 | return f.Read(b) | ||
198 | } | ||
199 | |||
200 | func (f *File) Truncate(size int64) error { | ||
201 | if f.closed == true { | ||
202 | return ErrFileClosed | ||
203 | } | ||
204 | if f.readOnly { | ||
205 | return &os.PathError{Op: "truncate", Path: f.fileData.name, Err: errors.New("file handle is read only")} | ||
206 | } | ||
207 | if size < 0 { | ||
208 | return ErrOutOfRange | ||
209 | } | ||
210 | if size > int64(len(f.fileData.data)) { | ||
211 | diff := size - int64(len(f.fileData.data)) | ||
212 | f.fileData.data = append(f.fileData.data, bytes.Repeat([]byte{00}, int(diff))...) | ||
213 | } else { | ||
214 | f.fileData.data = f.fileData.data[0:size] | ||
215 | } | ||
216 | setModTime(f.fileData, time.Now()) | ||
217 | return nil | ||
218 | } | ||
219 | |||
220 | func (f *File) Seek(offset int64, whence int) (int64, error) { | ||
221 | if f.closed == true { | ||
222 | return 0, ErrFileClosed | ||
223 | } | ||
224 | switch whence { | ||
225 | case 0: | ||
226 | atomic.StoreInt64(&f.at, offset) | ||
227 | case 1: | ||
228 | atomic.AddInt64(&f.at, int64(offset)) | ||
229 | case 2: | ||
230 | atomic.StoreInt64(&f.at, int64(len(f.fileData.data))+offset) | ||
231 | } | ||
232 | return f.at, nil | ||
233 | } | ||
234 | |||
235 | func (f *File) Write(b []byte) (n int, err error) { | ||
236 | if f.readOnly { | ||
237 | return 0, &os.PathError{Op: "write", Path: f.fileData.name, Err: errors.New("file handle is read only")} | ||
238 | } | ||
239 | n = len(b) | ||
240 | cur := atomic.LoadInt64(&f.at) | ||
241 | f.fileData.Lock() | ||
242 | defer f.fileData.Unlock() | ||
243 | diff := cur - int64(len(f.fileData.data)) | ||
244 | var tail []byte | ||
245 | if n+int(cur) < len(f.fileData.data) { | ||
246 | tail = f.fileData.data[n+int(cur):] | ||
247 | } | ||
248 | if diff > 0 { | ||
249 | f.fileData.data = append(bytes.Repeat([]byte{00}, int(diff)), b...) | ||
250 | f.fileData.data = append(f.fileData.data, tail...) | ||
251 | } else { | ||
252 | f.fileData.data = append(f.fileData.data[:cur], b...) | ||
253 | f.fileData.data = append(f.fileData.data, tail...) | ||
254 | } | ||
255 | setModTime(f.fileData, time.Now()) | ||
256 | |||
257 | atomic.StoreInt64(&f.at, int64(len(f.fileData.data))) | ||
258 | return | ||
259 | } | ||
260 | |||
261 | func (f *File) WriteAt(b []byte, off int64) (n int, err error) { | ||
262 | atomic.StoreInt64(&f.at, off) | ||
263 | return f.Write(b) | ||
264 | } | ||
265 | |||
266 | func (f *File) WriteString(s string) (ret int, err error) { | ||
267 | return f.Write([]byte(s)) | ||
268 | } | ||
269 | |||
270 | func (f *File) Info() *FileInfo { | ||
271 | return &FileInfo{f.fileData} | ||
272 | } | ||
273 | |||
274 | type FileInfo struct { | ||
275 | *FileData | ||
276 | } | ||
277 | |||
278 | // Implements os.FileInfo | ||
279 | func (s *FileInfo) Name() string { | ||
280 | s.Lock() | ||
281 | _, name := filepath.Split(s.name) | ||
282 | s.Unlock() | ||
283 | return name | ||
284 | } | ||
285 | func (s *FileInfo) Mode() os.FileMode { | ||
286 | s.Lock() | ||
287 | defer s.Unlock() | ||
288 | return s.mode | ||
289 | } | ||
290 | func (s *FileInfo) ModTime() time.Time { | ||
291 | s.Lock() | ||
292 | defer s.Unlock() | ||
293 | return s.modtime | ||
294 | } | ||
295 | func (s *FileInfo) IsDir() bool { | ||
296 | s.Lock() | ||
297 | defer s.Unlock() | ||
298 | return s.dir | ||
299 | } | ||
300 | func (s *FileInfo) Sys() interface{} { return nil } | ||
301 | func (s *FileInfo) Size() int64 { | ||
302 | if s.IsDir() { | ||
303 | return int64(42) | ||
304 | } | ||
305 | s.Lock() | ||
306 | defer s.Unlock() | ||
307 | return int64(len(s.data)) | ||
308 | } | ||
309 | |||
310 | var ( | ||
311 | ErrFileClosed = errors.New("File is closed") | ||
312 | ErrOutOfRange = errors.New("Out of range") | ||
313 | ErrTooLarge = errors.New("Too large") | ||
314 | ErrFileNotFound = os.ErrNotExist | ||
315 | ErrFileExists = os.ErrExist | ||
316 | ErrDestinationExists = os.ErrExist | ||
317 | ) | ||
diff --git a/vendor/github.com/spf13/afero/memmap.go b/vendor/github.com/spf13/afero/memmap.go new file mode 100644 index 0000000..09498e7 --- /dev/null +++ b/vendor/github.com/spf13/afero/memmap.go | |||
@@ -0,0 +1,365 @@ | |||
1 | // Copyright © 2014 Steve Francia <spf@spf13.com>. | ||
2 | // | ||
3 | // Licensed under the Apache License, Version 2.0 (the "License"); | ||
4 | // you may not use this file except in compliance with the License. | ||
5 | // You may obtain a copy of the License at | ||
6 | // http://www.apache.org/licenses/LICENSE-2.0 | ||
7 | // | ||
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 afero | ||
15 | |||
16 | import ( | ||
17 | "fmt" | ||
18 | "log" | ||
19 | "os" | ||
20 | "path/filepath" | ||
21 | "strings" | ||
22 | "sync" | ||
23 | "time" | ||
24 | |||
25 | "github.com/spf13/afero/mem" | ||
26 | ) | ||
27 | |||
28 | type MemMapFs struct { | ||
29 | mu sync.RWMutex | ||
30 | data map[string]*mem.FileData | ||
31 | init sync.Once | ||
32 | } | ||
33 | |||
34 | func NewMemMapFs() Fs { | ||
35 | return &MemMapFs{} | ||
36 | } | ||
37 | |||
38 | func (m *MemMapFs) getData() map[string]*mem.FileData { | ||
39 | m.init.Do(func() { | ||
40 | m.data = make(map[string]*mem.FileData) | ||
41 | // Root should always exist, right? | ||
42 | // TODO: what about windows? | ||
43 | m.data[FilePathSeparator] = mem.CreateDir(FilePathSeparator) | ||
44 | }) | ||
45 | return m.data | ||
46 | } | ||
47 | |||
48 | func (*MemMapFs) Name() string { return "MemMapFS" } | ||
49 | |||
50 | func (m *MemMapFs) Create(name string) (File, error) { | ||
51 | name = normalizePath(name) | ||
52 | m.mu.Lock() | ||
53 | file := mem.CreateFile(name) | ||
54 | m.getData()[name] = file | ||
55 | m.registerWithParent(file) | ||
56 | m.mu.Unlock() | ||
57 | return mem.NewFileHandle(file), nil | ||
58 | } | ||
59 | |||
60 | func (m *MemMapFs) unRegisterWithParent(fileName string) error { | ||
61 | f, err := m.lockfreeOpen(fileName) | ||
62 | if err != nil { | ||
63 | return err | ||
64 | } | ||
65 | parent := m.findParent(f) | ||
66 | if parent == nil { | ||
67 | log.Panic("parent of ", f.Name(), " is nil") | ||
68 | } | ||
69 | |||
70 | parent.Lock() | ||
71 | mem.RemoveFromMemDir(parent, f) | ||
72 | parent.Unlock() | ||
73 | return nil | ||
74 | } | ||
75 | |||
76 | func (m *MemMapFs) findParent(f *mem.FileData) *mem.FileData { | ||
77 | pdir, _ := filepath.Split(f.Name()) | ||
78 | pdir = filepath.Clean(pdir) | ||
79 | pfile, err := m.lockfreeOpen(pdir) | ||
80 | if err != nil { | ||
81 | return nil | ||
82 | } | ||
83 | return pfile | ||
84 | } | ||
85 | |||
86 | func (m *MemMapFs) registerWithParent(f *mem.FileData) { | ||
87 | if f == nil { | ||
88 | return | ||
89 | } | ||
90 | parent := m.findParent(f) | ||
91 | if parent == nil { | ||
92 | pdir := filepath.Dir(filepath.Clean(f.Name())) | ||
93 | err := m.lockfreeMkdir(pdir, 0777) | ||
94 | if err != nil { | ||
95 | //log.Println("Mkdir error:", err) | ||
96 | return | ||
97 | } | ||
98 | parent, err = m.lockfreeOpen(pdir) | ||
99 | if err != nil { | ||
100 | //log.Println("Open after Mkdir error:", err) | ||
101 | return | ||
102 | } | ||
103 | } | ||
104 | |||
105 | parent.Lock() | ||
106 | mem.InitializeDir(parent) | ||
107 | mem.AddToMemDir(parent, f) | ||
108 | parent.Unlock() | ||
109 | } | ||
110 | |||
111 | func (m *MemMapFs) lockfreeMkdir(name string, perm os.FileMode) error { | ||
112 | name = normalizePath(name) | ||
113 | x, ok := m.getData()[name] | ||
114 | if ok { | ||
115 | // Only return ErrFileExists if it's a file, not a directory. | ||
116 | i := mem.FileInfo{FileData: x} | ||
117 | if !i.IsDir() { | ||
118 | return ErrFileExists | ||
119 | } | ||
120 | } else { | ||
121 | item := mem.CreateDir(name) | ||
122 | m.getData()[name] = item | ||
123 | m.registerWithParent(item) | ||
124 | } | ||
125 | return nil | ||
126 | } | ||
127 | |||
128 | func (m *MemMapFs) Mkdir(name string, perm os.FileMode) error { | ||
129 | name = normalizePath(name) | ||
130 | |||
131 | m.mu.RLock() | ||
132 | _, ok := m.getData()[name] | ||
133 | m.mu.RUnlock() | ||
134 | if ok { | ||
135 | return &os.PathError{Op: "mkdir", Path: name, Err: ErrFileExists} | ||
136 | } | ||
137 | |||
138 | m.mu.Lock() | ||
139 | item := mem.CreateDir(name) | ||
140 | m.getData()[name] = item | ||
141 | m.registerWithParent(item) | ||
142 | m.mu.Unlock() | ||
143 | |||
144 | m.Chmod(name, perm|os.ModeDir) | ||
145 | |||
146 | return nil | ||
147 | } | ||
148 | |||
149 | func (m *MemMapFs) MkdirAll(path string, perm os.FileMode) error { | ||
150 | err := m.Mkdir(path, perm) | ||
151 | if err != nil { | ||
152 | if err.(*os.PathError).Err == ErrFileExists { | ||
153 | return nil | ||
154 | } | ||
155 | return err | ||
156 | } | ||
157 | return nil | ||
158 | } | ||
159 | |||
160 | // Handle some relative paths | ||
161 | func normalizePath(path string) string { | ||
162 | path = filepath.Clean(path) | ||
163 | |||
164 | switch path { | ||
165 | case ".": | ||
166 | return FilePathSeparator | ||
167 | case "..": | ||
168 | return FilePathSeparator | ||
169 | default: | ||
170 | return path | ||
171 | } | ||
172 | } | ||
173 | |||
174 | func (m *MemMapFs) Open(name string) (File, error) { | ||
175 | f, err := m.open(name) | ||
176 | if f != nil { | ||
177 | return mem.NewReadOnlyFileHandle(f), err | ||
178 | } | ||
179 | return nil, err | ||
180 | } | ||
181 | |||
182 | func (m *MemMapFs) openWrite(name string) (File, error) { | ||
183 | f, err := m.open(name) | ||
184 | if f != nil { | ||
185 | return mem.NewFileHandle(f), err | ||
186 | } | ||
187 | return nil, err | ||
188 | } | ||
189 | |||
190 | func (m *MemMapFs) open(name string) (*mem.FileData, error) { | ||
191 | name = normalizePath(name) | ||
192 | |||
193 | m.mu.RLock() | ||
194 | f, ok := m.getData()[name] | ||
195 | m.mu.RUnlock() | ||
196 | if !ok { | ||
197 | return nil, &os.PathError{Op: "open", Path: name, Err: ErrFileNotFound} | ||
198 | } | ||
199 | return f, nil | ||
200 | } | ||
201 | |||
202 | func (m *MemMapFs) lockfreeOpen(name string) (*mem.FileData, error) { | ||
203 | name = normalizePath(name) | ||
204 | f, ok := m.getData()[name] | ||
205 | if ok { | ||
206 | return f, nil | ||
207 | } else { | ||
208 | return nil, ErrFileNotFound | ||
209 | } | ||
210 | } | ||
211 | |||
212 | func (m *MemMapFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) { | ||
213 | chmod := false | ||
214 | file, err := m.openWrite(name) | ||
215 | if os.IsNotExist(err) && (flag&os.O_CREATE > 0) { | ||
216 | file, err = m.Create(name) | ||
217 | chmod = true | ||
218 | } | ||
219 | if err != nil { | ||
220 | return nil, err | ||
221 | } | ||
222 | if flag == os.O_RDONLY { | ||
223 | file = mem.NewReadOnlyFileHandle(file.(*mem.File).Data()) | ||
224 | } | ||
225 | if flag&os.O_APPEND > 0 { | ||
226 | _, err = file.Seek(0, os.SEEK_END) | ||
227 | if err != nil { | ||
228 | file.Close() | ||
229 | return nil, err | ||
230 | } | ||
231 | } | ||
232 | if flag&os.O_TRUNC > 0 && flag&(os.O_RDWR|os.O_WRONLY) > 0 { | ||
233 | err = file.Truncate(0) | ||
234 | if err != nil { | ||
235 | file.Close() | ||
236 | return nil, err | ||
237 | } | ||
238 | } | ||
239 | if chmod { | ||
240 | m.Chmod(name, perm) | ||
241 | } | ||
242 | return file, nil | ||
243 | } | ||
244 | |||
245 | func (m *MemMapFs) Remove(name string) error { | ||
246 | name = normalizePath(name) | ||
247 | |||
248 | m.mu.Lock() | ||
249 | defer m.mu.Unlock() | ||
250 | |||
251 | if _, ok := m.getData()[name]; ok { | ||
252 | err := m.unRegisterWithParent(name) | ||
253 | if err != nil { | ||
254 | return &os.PathError{Op: "remove", Path: name, Err: err} | ||
255 | } | ||
256 | delete(m.getData(), name) | ||
257 | } else { | ||
258 | return &os.PathError{Op: "remove", Path: name, Err: os.ErrNotExist} | ||
259 | } | ||
260 | return nil | ||
261 | } | ||
262 | |||
263 | func (m *MemMapFs) RemoveAll(path string) error { | ||
264 | path = normalizePath(path) | ||
265 | m.mu.Lock() | ||
266 | m.unRegisterWithParent(path) | ||
267 | m.mu.Unlock() | ||
268 | |||
269 | m.mu.RLock() | ||
270 | defer m.mu.RUnlock() | ||
271 | |||
272 | for p, _ := range m.getData() { | ||
273 | if strings.HasPrefix(p, path) { | ||
274 | m.mu.RUnlock() | ||
275 | m.mu.Lock() | ||
276 | delete(m.getData(), p) | ||
277 | m.mu.Unlock() | ||
278 | m.mu.RLock() | ||
279 | } | ||
280 | } | ||
281 | return nil | ||
282 | } | ||
283 | |||
284 | func (m *MemMapFs) Rename(oldname, newname string) error { | ||
285 | oldname = normalizePath(oldname) | ||
286 | newname = normalizePath(newname) | ||
287 | |||
288 | if oldname == newname { | ||
289 | return nil | ||
290 | } | ||
291 | |||
292 | m.mu.RLock() | ||
293 | defer m.mu.RUnlock() | ||
294 | if _, ok := m.getData()[oldname]; ok { | ||
295 | m.mu.RUnlock() | ||
296 | m.mu.Lock() | ||
297 | m.unRegisterWithParent(oldname) | ||
298 | fileData := m.getData()[oldname] | ||
299 | delete(m.getData(), oldname) | ||
300 | mem.ChangeFileName(fileData, newname) | ||
301 | m.getData()[newname] = fileData | ||
302 | m.registerWithParent(fileData) | ||
303 | m.mu.Unlock() | ||
304 | m.mu.RLock() | ||
305 | } else { | ||
306 | return &os.PathError{Op: "rename", Path: oldname, Err: ErrFileNotFound} | ||
307 | } | ||
308 | return nil | ||
309 | } | ||
310 | |||
311 | func (m *MemMapFs) Stat(name string) (os.FileInfo, error) { | ||
312 | f, err := m.Open(name) | ||
313 | if err != nil { | ||
314 | return nil, err | ||
315 | } | ||
316 | fi := mem.GetFileInfo(f.(*mem.File).Data()) | ||
317 | return fi, nil | ||
318 | } | ||
319 | |||
320 | func (m *MemMapFs) Chmod(name string, mode os.FileMode) error { | ||
321 | name = normalizePath(name) | ||
322 | |||
323 | m.mu.RLock() | ||
324 | f, ok := m.getData()[name] | ||
325 | m.mu.RUnlock() | ||
326 | if !ok { | ||
327 | return &os.PathError{Op: "chmod", Path: name, Err: ErrFileNotFound} | ||
328 | } | ||
329 | |||
330 | m.mu.Lock() | ||
331 | mem.SetMode(f, mode) | ||
332 | m.mu.Unlock() | ||
333 | |||
334 | return nil | ||
335 | } | ||
336 | |||
337 | func (m *MemMapFs) Chtimes(name string, atime time.Time, mtime time.Time) error { | ||
338 | name = normalizePath(name) | ||
339 | |||
340 | m.mu.RLock() | ||
341 | f, ok := m.getData()[name] | ||
342 | m.mu.RUnlock() | ||
343 | if !ok { | ||
344 | return &os.PathError{Op: "chtimes", Path: name, Err: ErrFileNotFound} | ||
345 | } | ||
346 | |||
347 | m.mu.Lock() | ||
348 | mem.SetModTime(f, mtime) | ||
349 | m.mu.Unlock() | ||
350 | |||
351 | return nil | ||
352 | } | ||
353 | |||
354 | func (m *MemMapFs) List() { | ||
355 | for _, x := range m.data { | ||
356 | y := mem.FileInfo{FileData: x} | ||
357 | fmt.Println(x.Name(), y.Size()) | ||
358 | } | ||
359 | } | ||
360 | |||
361 | // func debugMemMapList(fs Fs) { | ||
362 | // if x, ok := fs.(*MemMapFs); ok { | ||
363 | // x.List() | ||
364 | // } | ||
365 | // } | ||
diff --git a/vendor/github.com/spf13/afero/os.go b/vendor/github.com/spf13/afero/os.go new file mode 100644 index 0000000..13cc1b8 --- /dev/null +++ b/vendor/github.com/spf13/afero/os.go | |||
@@ -0,0 +1,101 @@ | |||
1 | // Copyright © 2014 Steve Francia <spf@spf13.com>. | ||
2 | // Copyright 2013 tsuru authors. All rights reserved. | ||
3 | // | ||
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 | // | ||
9 | // Unless required by applicable law or agreed to in writing, software | ||
10 | // distributed under the License is distributed on an "AS IS" BASIS, | ||
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
12 | // See the License for the specific language governing permissions and | ||
13 | // limitations under the License. | ||
14 | |||
15 | package afero | ||
16 | |||
17 | import ( | ||
18 | "os" | ||
19 | "time" | ||
20 | ) | ||
21 | |||
22 | var _ Lstater = (*OsFs)(nil) | ||
23 | |||
24 | // OsFs is a Fs implementation that uses functions provided by the os package. | ||
25 | // | ||
26 | // For details in any method, check the documentation of the os package | ||
27 | // (http://golang.org/pkg/os/). | ||
28 | type OsFs struct{} | ||
29 | |||
30 | func NewOsFs() Fs { | ||
31 | return &OsFs{} | ||
32 | } | ||
33 | |||
34 | func (OsFs) Name() string { return "OsFs" } | ||
35 | |||
36 | func (OsFs) Create(name string) (File, error) { | ||
37 | f, e := os.Create(name) | ||
38 | if f == nil { | ||
39 | // while this looks strange, we need to return a bare nil (of type nil) not | ||
40 | // a nil value of type *os.File or nil won't be nil | ||
41 | return nil, e | ||
42 | } | ||
43 | return f, e | ||
44 | } | ||
45 | |||
46 | func (OsFs) Mkdir(name string, perm os.FileMode) error { | ||
47 | return os.Mkdir(name, perm) | ||
48 | } | ||
49 | |||
50 | func (OsFs) MkdirAll(path string, perm os.FileMode) error { | ||
51 | return os.MkdirAll(path, perm) | ||
52 | } | ||
53 | |||
54 | func (OsFs) Open(name string) (File, error) { | ||
55 | f, e := os.Open(name) | ||
56 | if f == nil { | ||
57 | // while this looks strange, we need to return a bare nil (of type nil) not | ||
58 | // a nil value of type *os.File or nil won't be nil | ||
59 | return nil, e | ||
60 | } | ||
61 | return f, e | ||
62 | } | ||
63 | |||
64 | func (OsFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) { | ||
65 | f, e := os.OpenFile(name, flag, perm) | ||
66 | if f == nil { | ||
67 | // while this looks strange, we need to return a bare nil (of type nil) not | ||
68 | // a nil value of type *os.File or nil won't be nil | ||
69 | return nil, e | ||
70 | } | ||
71 | return f, e | ||
72 | } | ||
73 | |||
74 | func (OsFs) Remove(name string) error { | ||
75 | return os.Remove(name) | ||
76 | } | ||
77 | |||
78 | func (OsFs) RemoveAll(path string) error { | ||
79 | return os.RemoveAll(path) | ||
80 | } | ||
81 | |||
82 | func (OsFs) Rename(oldname, newname string) error { | ||
83 | return os.Rename(oldname, newname) | ||
84 | } | ||
85 | |||
86 | func (OsFs) Stat(name string) (os.FileInfo, error) { | ||
87 | return os.Stat(name) | ||
88 | } | ||
89 | |||
90 | func (OsFs) Chmod(name string, mode os.FileMode) error { | ||
91 | return os.Chmod(name, mode) | ||
92 | } | ||
93 | |||
94 | func (OsFs) Chtimes(name string, atime time.Time, mtime time.Time) error { | ||
95 | return os.Chtimes(name, atime, mtime) | ||
96 | } | ||
97 | |||
98 | func (OsFs) LstatIfPossible(name string) (os.FileInfo, bool, error) { | ||
99 | fi, err := os.Lstat(name) | ||
100 | return fi, true, err | ||
101 | } | ||
diff --git a/vendor/github.com/spf13/afero/path.go b/vendor/github.com/spf13/afero/path.go new file mode 100644 index 0000000..18f60a0 --- /dev/null +++ b/vendor/github.com/spf13/afero/path.go | |||
@@ -0,0 +1,106 @@ | |||
1 | // Copyright ©2015 The Go Authors | ||
2 | // Copyright ©2015 Steve Francia <spf@spf13.com> | ||
3 | // | ||
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 | // | ||
8 | // http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | // | ||
10 | // Unless required by applicable law or agreed to in writing, software | ||
11 | // distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | // See the License for the specific language governing permissions and | ||
14 | // limitations under the License. | ||
15 | |||
16 | package afero | ||
17 | |||
18 | import ( | ||
19 | "os" | ||
20 | "path/filepath" | ||
21 | "sort" | ||
22 | ) | ||
23 | |||
24 | // readDirNames reads the directory named by dirname and returns | ||
25 | // a sorted list of directory entries. | ||
26 | // adapted from https://golang.org/src/path/filepath/path.go | ||
27 | func readDirNames(fs Fs, dirname string) ([]string, error) { | ||
28 | f, err := fs.Open(dirname) | ||
29 | if err != nil { | ||
30 | return nil, err | ||
31 | } | ||
32 | names, err := f.Readdirnames(-1) | ||
33 | f.Close() | ||
34 | if err != nil { | ||
35 | return nil, err | ||
36 | } | ||
37 | sort.Strings(names) | ||
38 | return names, nil | ||
39 | } | ||
40 | |||
41 | // walk recursively descends path, calling walkFn | ||
42 | // adapted from https://golang.org/src/path/filepath/path.go | ||
43 | func walk(fs Fs, path string, info os.FileInfo, walkFn filepath.WalkFunc) error { | ||
44 | err := walkFn(path, info, nil) | ||
45 | if err != nil { | ||
46 | if info.IsDir() && err == filepath.SkipDir { | ||
47 | return nil | ||
48 | } | ||
49 | return err | ||
50 | } | ||
51 | |||
52 | if !info.IsDir() { | ||
53 | return nil | ||
54 | } | ||
55 | |||
56 | names, err := readDirNames(fs, path) | ||
57 | if err != nil { | ||
58 | return walkFn(path, info, err) | ||
59 | } | ||
60 | |||
61 | for _, name := range names { | ||
62 | filename := filepath.Join(path, name) | ||
63 | fileInfo, err := lstatIfPossible(fs, filename) | ||
64 | if err != nil { | ||
65 | if err := walkFn(filename, fileInfo, err); err != nil && err != filepath.SkipDir { | ||
66 | return err | ||
67 | } | ||
68 | } else { | ||
69 | err = walk(fs, filename, fileInfo, walkFn) | ||
70 | if err != nil { | ||
71 | if !fileInfo.IsDir() || err != filepath.SkipDir { | ||
72 | return err | ||
73 | } | ||
74 | } | ||
75 | } | ||
76 | } | ||
77 | return nil | ||
78 | } | ||
79 | |||
80 | // if the filesystem supports it, use Lstat, else use fs.Stat | ||
81 | func lstatIfPossible(fs Fs, path string) (os.FileInfo, error) { | ||
82 | if lfs, ok := fs.(Lstater); ok { | ||
83 | fi, _, err := lfs.LstatIfPossible(path) | ||
84 | return fi, err | ||
85 | } | ||
86 | return fs.Stat(path) | ||
87 | } | ||
88 | |||
89 | // Walk walks the file tree rooted at root, calling walkFn for each file or | ||
90 | // directory in the tree, including root. All errors that arise visiting files | ||
91 | // and directories are filtered by walkFn. The files are walked in lexical | ||
92 | // order, which makes the output deterministic but means that for very | ||
93 | // large directories Walk can be inefficient. | ||
94 | // Walk does not follow symbolic links. | ||
95 | |||
96 | func (a Afero) Walk(root string, walkFn filepath.WalkFunc) error { | ||
97 | return Walk(a.Fs, root, walkFn) | ||
98 | } | ||
99 | |||
100 | func Walk(fs Fs, root string, walkFn filepath.WalkFunc) error { | ||
101 | info, err := lstatIfPossible(fs, root) | ||
102 | if err != nil { | ||
103 | return walkFn(root, nil, err) | ||
104 | } | ||
105 | return walk(fs, root, info, walkFn) | ||
106 | } | ||
diff --git a/vendor/github.com/spf13/afero/readonlyfs.go b/vendor/github.com/spf13/afero/readonlyfs.go new file mode 100644 index 0000000..c6376ec --- /dev/null +++ b/vendor/github.com/spf13/afero/readonlyfs.go | |||
@@ -0,0 +1,80 @@ | |||
1 | package afero | ||
2 | |||
3 | import ( | ||
4 | "os" | ||
5 | "syscall" | ||
6 | "time" | ||
7 | ) | ||
8 | |||
9 | var _ Lstater = (*ReadOnlyFs)(nil) | ||
10 | |||
11 | type ReadOnlyFs struct { | ||
12 | source Fs | ||
13 | } | ||
14 | |||
15 | func NewReadOnlyFs(source Fs) Fs { | ||
16 | return &ReadOnlyFs{source: source} | ||
17 | } | ||
18 | |||
19 | func (r *ReadOnlyFs) ReadDir(name string) ([]os.FileInfo, error) { | ||
20 | return ReadDir(r.source, name) | ||
21 | } | ||
22 | |||
23 | func (r *ReadOnlyFs) Chtimes(n string, a, m time.Time) error { | ||
24 | return syscall.EPERM | ||
25 | } | ||
26 | |||
27 | func (r *ReadOnlyFs) Chmod(n string, m os.FileMode) error { | ||
28 | return syscall.EPERM | ||
29 | } | ||
30 | |||
31 | func (r *ReadOnlyFs) Name() string { | ||
32 | return "ReadOnlyFilter" | ||
33 | } | ||
34 | |||
35 | func (r *ReadOnlyFs) Stat(name string) (os.FileInfo, error) { | ||
36 | return r.source.Stat(name) | ||
37 | } | ||
38 | |||
39 | func (r *ReadOnlyFs) LstatIfPossible(name string) (os.FileInfo, bool, error) { | ||
40 | if lsf, ok := r.source.(Lstater); ok { | ||
41 | return lsf.LstatIfPossible(name) | ||
42 | } | ||
43 | fi, err := r.Stat(name) | ||
44 | return fi, false, err | ||
45 | } | ||
46 | |||
47 | func (r *ReadOnlyFs) Rename(o, n string) error { | ||
48 | return syscall.EPERM | ||
49 | } | ||
50 | |||
51 | func (r *ReadOnlyFs) RemoveAll(p string) error { | ||
52 | return syscall.EPERM | ||
53 | } | ||
54 | |||
55 | func (r *ReadOnlyFs) Remove(n string) error { | ||
56 | return syscall.EPERM | ||
57 | } | ||
58 | |||
59 | func (r *ReadOnlyFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) { | ||
60 | if flag&(os.O_WRONLY|syscall.O_RDWR|os.O_APPEND|os.O_CREATE|os.O_TRUNC) != 0 { | ||
61 | return nil, syscall.EPERM | ||
62 | } | ||
63 | return r.source.OpenFile(name, flag, perm) | ||
64 | } | ||
65 | |||
66 | func (r *ReadOnlyFs) Open(n string) (File, error) { | ||
67 | return r.source.Open(n) | ||
68 | } | ||
69 | |||
70 | func (r *ReadOnlyFs) Mkdir(n string, p os.FileMode) error { | ||
71 | return syscall.EPERM | ||
72 | } | ||
73 | |||
74 | func (r *ReadOnlyFs) MkdirAll(n string, p os.FileMode) error { | ||
75 | return syscall.EPERM | ||
76 | } | ||
77 | |||
78 | func (r *ReadOnlyFs) Create(n string) (File, error) { | ||
79 | return nil, syscall.EPERM | ||
80 | } | ||
diff --git a/vendor/github.com/spf13/afero/regexpfs.go b/vendor/github.com/spf13/afero/regexpfs.go new file mode 100644 index 0000000..9d92dbc --- /dev/null +++ b/vendor/github.com/spf13/afero/regexpfs.go | |||
@@ -0,0 +1,214 @@ | |||
1 | package afero | ||
2 | |||
3 | import ( | ||
4 | "os" | ||
5 | "regexp" | ||
6 | "syscall" | ||
7 | "time" | ||
8 | ) | ||
9 | |||
10 | // The RegexpFs filters files (not directories) by regular expression. Only | ||
11 | // files matching the given regexp will be allowed, all others get a ENOENT error ( | ||
12 | // "No such file or directory"). | ||
13 | // | ||
14 | type RegexpFs struct { | ||
15 | re *regexp.Regexp | ||
16 | source Fs | ||
17 | } | ||
18 | |||
19 | func NewRegexpFs(source Fs, re *regexp.Regexp) Fs { | ||
20 | return &RegexpFs{source: source, re: re} | ||
21 | } | ||
22 | |||
23 | type RegexpFile struct { | ||
24 | f File | ||
25 | re *regexp.Regexp | ||
26 | } | ||
27 | |||
28 | func (r *RegexpFs) matchesName(name string) error { | ||
29 | if r.re == nil { | ||
30 | return nil | ||
31 | } | ||
32 | if r.re.MatchString(name) { | ||
33 | return nil | ||
34 | } | ||
35 | return syscall.ENOENT | ||
36 | } | ||
37 | |||
38 | func (r *RegexpFs) dirOrMatches(name string) error { | ||
39 | dir, err := IsDir(r.source, name) | ||
40 | if err != nil { | ||
41 | return err | ||
42 | } | ||
43 | if dir { | ||
44 | return nil | ||
45 | } | ||
46 | return r.matchesName(name) | ||
47 | } | ||
48 | |||
49 | func (r *RegexpFs) Chtimes(name string, a, m time.Time) error { | ||
50 | if err := r.dirOrMatches(name); err != nil { | ||
51 | return err | ||
52 | } | ||
53 | return r.source.Chtimes(name, a, m) | ||
54 | } | ||
55 | |||
56 | func (r *RegexpFs) Chmod(name string, mode os.FileMode) error { | ||
57 | if err := r.dirOrMatches(name); err != nil { | ||
58 | return err | ||
59 | } | ||
60 | return r.source.Chmod(name, mode) | ||
61 | } | ||
62 | |||
63 | func (r *RegexpFs) Name() string { | ||
64 | return "RegexpFs" | ||
65 | } | ||
66 | |||
67 | func (r *RegexpFs) Stat(name string) (os.FileInfo, error) { | ||
68 | if err := r.dirOrMatches(name); err != nil { | ||
69 | return nil, err | ||
70 | } | ||
71 | return r.source.Stat(name) | ||
72 | } | ||
73 | |||
74 | func (r *RegexpFs) Rename(oldname, newname string) error { | ||
75 | dir, err := IsDir(r.source, oldname) | ||
76 | if err != nil { | ||
77 | return err | ||
78 | } | ||
79 | if dir { | ||
80 | return nil | ||
81 | } | ||
82 | if err := r.matchesName(oldname); err != nil { | ||
83 | return err | ||
84 | } | ||
85 | if err := r.matchesName(newname); err != nil { | ||
86 | return err | ||
87 | } | ||
88 | return r.source.Rename(oldname, newname) | ||
89 | } | ||
90 | |||
91 | func (r *RegexpFs) RemoveAll(p string) error { | ||
92 | dir, err := IsDir(r.source, p) | ||
93 | if err != nil { | ||
94 | return err | ||
95 | } | ||
96 | if !dir { | ||
97 | if err := r.matchesName(p); err != nil { | ||
98 | return err | ||
99 | } | ||
100 | } | ||
101 | return r.source.RemoveAll(p) | ||
102 | } | ||
103 | |||
104 | func (r *RegexpFs) Remove(name string) error { | ||
105 | if err := r.dirOrMatches(name); err != nil { | ||
106 | return err | ||
107 | } | ||
108 | return r.source.Remove(name) | ||
109 | } | ||
110 | |||
111 | func (r *RegexpFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) { | ||
112 | if err := r.dirOrMatches(name); err != nil { | ||
113 | return nil, err | ||
114 | } | ||
115 | return r.source.OpenFile(name, flag, perm) | ||
116 | } | ||
117 | |||
118 | func (r *RegexpFs) Open(name string) (File, error) { | ||
119 | dir, err := IsDir(r.source, name) | ||
120 | if err != nil { | ||
121 | return nil, err | ||
122 | } | ||
123 | if !dir { | ||
124 | if err := r.matchesName(name); err != nil { | ||
125 | return nil, err | ||
126 | } | ||
127 | } | ||
128 | f, err := r.source.Open(name) | ||
129 | return &RegexpFile{f: f, re: r.re}, nil | ||
130 | } | ||
131 | |||
132 | func (r *RegexpFs) Mkdir(n string, p os.FileMode) error { | ||
133 | return r.source.Mkdir(n, p) | ||
134 | } | ||
135 | |||
136 | func (r *RegexpFs) MkdirAll(n string, p os.FileMode) error { | ||
137 | return r.source.MkdirAll(n, p) | ||
138 | } | ||
139 | |||
140 | func (r *RegexpFs) Create(name string) (File, error) { | ||
141 | if err := r.matchesName(name); err != nil { | ||
142 | return nil, err | ||
143 | } | ||
144 | return r.source.Create(name) | ||
145 | } | ||
146 | |||
147 | func (f *RegexpFile) Close() error { | ||
148 | return f.f.Close() | ||
149 | } | ||
150 | |||
151 | func (f *RegexpFile) Read(s []byte) (int, error) { | ||
152 | return f.f.Read(s) | ||
153 | } | ||
154 | |||
155 | func (f *RegexpFile) ReadAt(s []byte, o int64) (int, error) { | ||
156 | return f.f.ReadAt(s, o) | ||
157 | } | ||
158 | |||
159 | func (f *RegexpFile) Seek(o int64, w int) (int64, error) { | ||
160 | return f.f.Seek(o, w) | ||
161 | } | ||
162 | |||
163 | func (f *RegexpFile) Write(s []byte) (int, error) { | ||
164 | return f.f.Write(s) | ||
165 | } | ||
166 | |||
167 | func (f *RegexpFile) WriteAt(s []byte, o int64) (int, error) { | ||
168 | return f.f.WriteAt(s, o) | ||
169 | } | ||
170 | |||
171 | func (f *RegexpFile) Name() string { | ||
172 | return f.f.Name() | ||
173 | } | ||
174 | |||
175 | func (f *RegexpFile) Readdir(c int) (fi []os.FileInfo, err error) { | ||
176 | var rfi []os.FileInfo | ||
177 | rfi, err = f.f.Readdir(c) | ||
178 | if err != nil { | ||
179 | return nil, err | ||
180 | } | ||
181 | for _, i := range rfi { | ||
182 | if i.IsDir() || f.re.MatchString(i.Name()) { | ||
183 | fi = append(fi, i) | ||
184 | } | ||
185 | } | ||
186 | return fi, nil | ||
187 | } | ||
188 | |||
189 | func (f *RegexpFile) Readdirnames(c int) (n []string, err error) { | ||
190 | fi, err := f.Readdir(c) | ||
191 | if err != nil { | ||
192 | return nil, err | ||
193 | } | ||
194 | for _, s := range fi { | ||
195 | n = append(n, s.Name()) | ||
196 | } | ||
197 | return n, nil | ||
198 | } | ||
199 | |||
200 | func (f *RegexpFile) Stat() (os.FileInfo, error) { | ||
201 | return f.f.Stat() | ||
202 | } | ||
203 | |||
204 | func (f *RegexpFile) Sync() error { | ||
205 | return f.f.Sync() | ||
206 | } | ||
207 | |||
208 | func (f *RegexpFile) Truncate(s int64) error { | ||
209 | return f.f.Truncate(s) | ||
210 | } | ||
211 | |||
212 | func (f *RegexpFile) WriteString(s string) (int, error) { | ||
213 | return f.f.WriteString(s) | ||
214 | } | ||
diff --git a/vendor/github.com/spf13/afero/unionFile.go b/vendor/github.com/spf13/afero/unionFile.go new file mode 100644 index 0000000..abcf12d --- /dev/null +++ b/vendor/github.com/spf13/afero/unionFile.go | |||
@@ -0,0 +1,316 @@ | |||
1 | package afero | ||
2 | |||
3 | import ( | ||
4 | "io" | ||
5 | "os" | ||
6 | "path/filepath" | ||
7 | "syscall" | ||
8 | ) | ||
9 | |||
10 | // The UnionFile implements the afero.File interface and will be returned | ||
11 | // when reading a directory present at least in the overlay or opening a file | ||
12 | // for writing. | ||
13 | // | ||
14 | // The calls to | ||
15 | // Readdir() and Readdirnames() merge the file os.FileInfo / names from the | ||
16 | // base and the overlay - for files present in both layers, only those | ||
17 | // from the overlay will be used. | ||
18 | // | ||
19 | // When opening files for writing (Create() / OpenFile() with the right flags) | ||
20 | // the operations will be done in both layers, starting with the overlay. A | ||
21 | // successful read in the overlay will move the cursor position in the base layer | ||
22 | // by the number of bytes read. | ||
23 | type UnionFile struct { | ||
24 | Base File | ||
25 | Layer File | ||
26 | Merger DirsMerger | ||
27 | off int | ||
28 | files []os.FileInfo | ||
29 | } | ||
30 | |||
31 | func (f *UnionFile) Close() error { | ||
32 | // first close base, so we have a newer timestamp in the overlay. If we'd close | ||
33 | // the overlay first, we'd get a cacheStale the next time we access this file | ||
34 | // -> cache would be useless ;-) | ||
35 | if f.Base != nil { | ||
36 | f.Base.Close() | ||
37 | } | ||
38 | if f.Layer != nil { | ||
39 | return f.Layer.Close() | ||
40 | } | ||
41 | return BADFD | ||
42 | } | ||
43 | |||
44 | func (f *UnionFile) Read(s []byte) (int, error) { | ||
45 | if f.Layer != nil { | ||
46 | n, err := f.Layer.Read(s) | ||
47 | if (err == nil || err == io.EOF) && f.Base != nil { | ||
48 | // advance the file position also in the base file, the next | ||
49 | // call may be a write at this position (or a seek with SEEK_CUR) | ||
50 | if _, seekErr := f.Base.Seek(int64(n), os.SEEK_CUR); seekErr != nil { | ||
51 | // only overwrite err in case the seek fails: we need to | ||
52 | // report an eventual io.EOF to the caller | ||
53 | err = seekErr | ||
54 | } | ||
55 | } | ||
56 | return n, err | ||
57 | } | ||
58 | if f.Base != nil { | ||
59 | return f.Base.Read(s) | ||
60 | } | ||
61 | return 0, BADFD | ||
62 | } | ||
63 | |||
64 | func (f *UnionFile) ReadAt(s []byte, o int64) (int, error) { | ||
65 | if f.Layer != nil { | ||
66 | n, err := f.Layer.ReadAt(s, o) | ||
67 | if (err == nil || err == io.EOF) && f.Base != nil { | ||
68 | _, err = f.Base.Seek(o+int64(n), os.SEEK_SET) | ||
69 | } | ||
70 | return n, err | ||
71 | } | ||
72 | if f.Base != nil { | ||
73 | return f.Base.ReadAt(s, o) | ||
74 | } | ||
75 | return 0, BADFD | ||
76 | } | ||
77 | |||
78 | func (f *UnionFile) Seek(o int64, w int) (pos int64, err error) { | ||
79 | if f.Layer != nil { | ||
80 | pos, err = f.Layer.Seek(o, w) | ||
81 | if (err == nil || err == io.EOF) && f.Base != nil { | ||
82 | _, err = f.Base.Seek(o, w) | ||
83 | } | ||
84 | return pos, err | ||
85 | } | ||
86 | if f.Base != nil { | ||
87 | return f.Base.Seek(o, w) | ||
88 | } | ||
89 | return 0, BADFD | ||
90 | } | ||
91 | |||
92 | func (f *UnionFile) Write(s []byte) (n int, err error) { | ||
93 | if f.Layer != nil { | ||
94 | n, err = f.Layer.Write(s) | ||
95 | if err == nil && f.Base != nil { // hmm, do we have fixed size files where a write may hit the EOF mark? | ||
96 | _, err = f.Base.Write(s) | ||
97 | } | ||
98 | return n, err | ||
99 | } | ||
100 | if f.Base != nil { | ||
101 | return f.Base.Write(s) | ||
102 | } | ||
103 | return 0, BADFD | ||
104 | } | ||
105 | |||
106 | func (f *UnionFile) WriteAt(s []byte, o int64) (n int, err error) { | ||
107 | if f.Layer != nil { | ||
108 | n, err = f.Layer.WriteAt(s, o) | ||
109 | if err == nil && f.Base != nil { | ||
110 | _, err = f.Base.WriteAt(s, o) | ||
111 | } | ||
112 | return n, err | ||
113 | } | ||
114 | if f.Base != nil { | ||
115 | return f.Base.WriteAt(s, o) | ||
116 | } | ||
117 | return 0, BADFD | ||
118 | } | ||
119 | |||
120 | func (f *UnionFile) Name() string { | ||
121 | if f.Layer != nil { | ||
122 | return f.Layer.Name() | ||
123 | } | ||
124 | return f.Base.Name() | ||
125 | } | ||
126 | |||
127 | // DirsMerger is how UnionFile weaves two directories together. | ||
128 | // It takes the FileInfo slices from the layer and the base and returns a | ||
129 | // single view. | ||
130 | type DirsMerger func(lofi, bofi []os.FileInfo) ([]os.FileInfo, error) | ||
131 | |||
132 | var defaultUnionMergeDirsFn = func(lofi, bofi []os.FileInfo) ([]os.FileInfo, error) { | ||
133 | var files = make(map[string]os.FileInfo) | ||
134 | |||
135 | for _, fi := range lofi { | ||
136 | files[fi.Name()] = fi | ||
137 | } | ||
138 | |||
139 | for _, fi := range bofi { | ||
140 | if _, exists := files[fi.Name()]; !exists { | ||
141 | files[fi.Name()] = fi | ||
142 | } | ||
143 | } | ||
144 | |||
145 | rfi := make([]os.FileInfo, len(files)) | ||
146 | |||
147 | i := 0 | ||
148 | for _, fi := range files { | ||
149 | rfi[i] = fi | ||
150 | i++ | ||
151 | } | ||
152 | |||
153 | return rfi, nil | ||
154 | |||
155 | } | ||
156 | |||
157 | // Readdir will weave the two directories together and | ||
158 | // return a single view of the overlayed directories | ||
159 | // At the end of the directory view, the error is io.EOF. | ||
160 | func (f *UnionFile) Readdir(c int) (ofi []os.FileInfo, err error) { | ||
161 | var merge DirsMerger = f.Merger | ||
162 | if merge == nil { | ||
163 | merge = defaultUnionMergeDirsFn | ||
164 | } | ||
165 | |||
166 | if f.off == 0 { | ||
167 | var lfi []os.FileInfo | ||
168 | if f.Layer != nil { | ||
169 | lfi, err = f.Layer.Readdir(-1) | ||
170 | if err != nil { | ||
171 | return nil, err | ||
172 | } | ||
173 | } | ||
174 | |||
175 | var bfi []os.FileInfo | ||
176 | if f.Base != nil { | ||
177 | bfi, err = f.Base.Readdir(-1) | ||
178 | if err != nil { | ||
179 | return nil, err | ||
180 | } | ||
181 | |||
182 | } | ||
183 | merged, err := merge(lfi, bfi) | ||
184 | if err != nil { | ||
185 | return nil, err | ||
186 | } | ||
187 | f.files = append(f.files, merged...) | ||
188 | } | ||
189 | |||
190 | if f.off >= len(f.files) { | ||
191 | return nil, io.EOF | ||
192 | } | ||
193 | |||
194 | if c == -1 { | ||
195 | return f.files[f.off:], nil | ||
196 | } | ||
197 | |||
198 | if c > len(f.files) { | ||
199 | c = len(f.files) | ||
200 | } | ||
201 | |||
202 | defer func() { f.off += c }() | ||
203 | return f.files[f.off:c], nil | ||
204 | } | ||
205 | |||
206 | func (f *UnionFile) Readdirnames(c int) ([]string, error) { | ||
207 | rfi, err := f.Readdir(c) | ||
208 | if err != nil { | ||
209 | return nil, err | ||
210 | } | ||
211 | var names []string | ||
212 | for _, fi := range rfi { | ||
213 | names = append(names, fi.Name()) | ||
214 | } | ||
215 | return names, nil | ||
216 | } | ||
217 | |||
218 | func (f *UnionFile) Stat() (os.FileInfo, error) { | ||
219 | if f.Layer != nil { | ||
220 | return f.Layer.Stat() | ||
221 | } | ||
222 | if f.Base != nil { | ||
223 | return f.Base.Stat() | ||
224 | } | ||
225 | return nil, BADFD | ||
226 | } | ||
227 | |||
228 | func (f *UnionFile) Sync() (err error) { | ||
229 | if f.Layer != nil { | ||
230 | err = f.Layer.Sync() | ||
231 | if err == nil && f.Base != nil { | ||
232 | err = f.Base.Sync() | ||
233 | } | ||
234 | return err | ||
235 | } | ||
236 | if f.Base != nil { | ||
237 | return f.Base.Sync() | ||
238 | } | ||
239 | return BADFD | ||
240 | } | ||
241 | |||
242 | func (f *UnionFile) Truncate(s int64) (err error) { | ||
243 | if f.Layer != nil { | ||
244 | err = f.Layer.Truncate(s) | ||
245 | if err == nil && f.Base != nil { | ||
246 | err = f.Base.Truncate(s) | ||
247 | } | ||
248 | return err | ||
249 | } | ||
250 | if f.Base != nil { | ||
251 | return f.Base.Truncate(s) | ||
252 | } | ||
253 | return BADFD | ||
254 | } | ||
255 | |||
256 | func (f *UnionFile) WriteString(s string) (n int, err error) { | ||
257 | if f.Layer != nil { | ||
258 | n, err = f.Layer.WriteString(s) | ||
259 | if err == nil && f.Base != nil { | ||
260 | _, err = f.Base.WriteString(s) | ||
261 | } | ||
262 | return n, err | ||
263 | } | ||
264 | if f.Base != nil { | ||
265 | return f.Base.WriteString(s) | ||
266 | } | ||
267 | return 0, BADFD | ||
268 | } | ||
269 | |||
270 | func copyToLayer(base Fs, layer Fs, name string) error { | ||
271 | bfh, err := base.Open(name) | ||
272 | if err != nil { | ||
273 | return err | ||
274 | } | ||
275 | defer bfh.Close() | ||
276 | |||
277 | // First make sure the directory exists | ||
278 | exists, err := Exists(layer, filepath.Dir(name)) | ||
279 | if err != nil { | ||
280 | return err | ||
281 | } | ||
282 | if !exists { | ||
283 | err = layer.MkdirAll(filepath.Dir(name), 0777) // FIXME? | ||
284 | if err != nil { | ||
285 | return err | ||
286 | } | ||
287 | } | ||
288 | |||
289 | // Create the file on the overlay | ||
290 | lfh, err := layer.Create(name) | ||
291 | if err != nil { | ||
292 | return err | ||
293 | } | ||
294 | n, err := io.Copy(lfh, bfh) | ||
295 | if err != nil { | ||
296 | // If anything fails, clean up the file | ||
297 | layer.Remove(name) | ||
298 | lfh.Close() | ||
299 | return err | ||
300 | } | ||
301 | |||
302 | bfi, err := bfh.Stat() | ||
303 | if err != nil || bfi.Size() != n { | ||
304 | layer.Remove(name) | ||
305 | lfh.Close() | ||
306 | return syscall.EIO | ||
307 | } | ||
308 | |||
309 | err = lfh.Close() | ||
310 | if err != nil { | ||
311 | layer.Remove(name) | ||
312 | lfh.Close() | ||
313 | return err | ||
314 | } | ||
315 | return layer.Chtimes(name, bfi.ModTime(), bfi.ModTime()) | ||
316 | } | ||
diff --git a/vendor/github.com/spf13/afero/util.go b/vendor/github.com/spf13/afero/util.go new file mode 100644 index 0000000..4f253f4 --- /dev/null +++ b/vendor/github.com/spf13/afero/util.go | |||
@@ -0,0 +1,330 @@ | |||
1 | // Copyright ©2015 Steve Francia <spf@spf13.com> | ||
2 | // Portions Copyright ©2015 The Hugo Authors | ||
3 | // Portions Copyright 2016-present Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | ||
4 | // | ||
5 | // Licensed under the Apache License, Version 2.0 (the "License"); | ||
6 | // you may not use this file except in compliance with the License. | ||
7 | // You may obtain a copy of the License at | ||
8 | // | ||
9 | // http://www.apache.org/licenses/LICENSE-2.0 | ||
10 | // | ||
11 | // Unless required by applicable law or agreed to in writing, software | ||
12 | // distributed under the License is distributed on an "AS IS" BASIS, | ||
13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
14 | // See the License for the specific language governing permissions and | ||
15 | // limitations under the License. | ||
16 | |||
17 | package afero | ||
18 | |||
19 | import ( | ||
20 | "bytes" | ||
21 | "fmt" | ||
22 | "io" | ||
23 | "os" | ||
24 | "path/filepath" | ||
25 | "strings" | ||
26 | "unicode" | ||
27 | |||
28 | "golang.org/x/text/transform" | ||
29 | "golang.org/x/text/unicode/norm" | ||
30 | ) | ||
31 | |||
32 | // Filepath separator defined by os.Separator. | ||
33 | const FilePathSeparator = string(filepath.Separator) | ||
34 | |||
35 | // Takes a reader and a path and writes the content | ||
36 | func (a Afero) WriteReader(path string, r io.Reader) (err error) { | ||
37 | return WriteReader(a.Fs, path, r) | ||
38 | } | ||
39 | |||
40 | func WriteReader(fs Fs, path string, r io.Reader) (err error) { | ||
41 | dir, _ := filepath.Split(path) | ||
42 | ospath := filepath.FromSlash(dir) | ||
43 | |||
44 | if ospath != "" { | ||
45 | err = fs.MkdirAll(ospath, 0777) // rwx, rw, r | ||
46 | if err != nil { | ||
47 | if err != os.ErrExist { | ||
48 | return err | ||
49 | } | ||
50 | } | ||
51 | } | ||
52 | |||
53 | file, err := fs.Create(path) | ||
54 | if err != nil { | ||
55 | return | ||
56 | } | ||
57 | defer file.Close() | ||
58 | |||
59 | _, err = io.Copy(file, r) | ||
60 | return | ||
61 | } | ||
62 | |||
63 | // Same as WriteReader but checks to see if file/directory already exists. | ||
64 | func (a Afero) SafeWriteReader(path string, r io.Reader) (err error) { | ||
65 | return SafeWriteReader(a.Fs, path, r) | ||
66 | } | ||
67 | |||
68 | func SafeWriteReader(fs Fs, path string, r io.Reader) (err error) { | ||
69 | dir, _ := filepath.Split(path) | ||
70 | ospath := filepath.FromSlash(dir) | ||
71 | |||
72 | if ospath != "" { | ||
73 | err = fs.MkdirAll(ospath, 0777) // rwx, rw, r | ||
74 | if err != nil { | ||
75 | return | ||
76 | } | ||
77 | } | ||
78 | |||
79 | exists, err := Exists(fs, path) | ||
80 | if err != nil { | ||
81 | return | ||
82 | } | ||
83 | if exists { | ||
84 | return fmt.Errorf("%v already exists", path) | ||
85 | } | ||
86 | |||
87 | file, err := fs.Create(path) | ||
88 | if err != nil { | ||
89 | return | ||
90 | } | ||
91 | defer file.Close() | ||
92 | |||
93 | _, err = io.Copy(file, r) | ||
94 | return | ||
95 | } | ||
96 | |||
97 | func (a Afero) GetTempDir(subPath string) string { | ||
98 | return GetTempDir(a.Fs, subPath) | ||
99 | } | ||
100 | |||
101 | // GetTempDir returns the default temp directory with trailing slash | ||
102 | // if subPath is not empty then it will be created recursively with mode 777 rwx rwx rwx | ||
103 | func GetTempDir(fs Fs, subPath string) string { | ||
104 | addSlash := func(p string) string { | ||
105 | if FilePathSeparator != p[len(p)-1:] { | ||
106 | p = p + FilePathSeparator | ||
107 | } | ||
108 | return p | ||
109 | } | ||
110 | dir := addSlash(os.TempDir()) | ||
111 | |||
112 | if subPath != "" { | ||
113 | // preserve windows backslash :-( | ||
114 | if FilePathSeparator == "\\" { | ||
115 | subPath = strings.Replace(subPath, "\\", "____", -1) | ||
116 | } | ||
117 | dir = dir + UnicodeSanitize((subPath)) | ||
118 | if FilePathSeparator == "\\" { | ||
119 | dir = strings.Replace(dir, "____", "\\", -1) | ||
120 | } | ||
121 | |||
122 | if exists, _ := Exists(fs, dir); exists { | ||
123 | return addSlash(dir) | ||
124 | } | ||
125 | |||
126 | err := fs.MkdirAll(dir, 0777) | ||
127 | if err != nil { | ||
128 | panic(err) | ||
129 | } | ||
130 | dir = addSlash(dir) | ||
131 | } | ||
132 | return dir | ||
133 | } | ||
134 | |||
135 | // Rewrite string to remove non-standard path characters | ||
136 | func UnicodeSanitize(s string) string { | ||
137 | source := []rune(s) | ||
138 | target := make([]rune, 0, len(source)) | ||
139 | |||
140 | for _, r := range source { | ||
141 | if unicode.IsLetter(r) || | ||
142 | unicode.IsDigit(r) || | ||
143 | unicode.IsMark(r) || | ||
144 | r == '.' || | ||
145 | r == '/' || | ||
146 | r == '\\' || | ||
147 | r == '_' || | ||
148 | r == '-' || | ||
149 | r == '%' || | ||
150 | r == ' ' || | ||
151 | r == '#' { | ||
152 | target = append(target, r) | ||
153 | } | ||
154 | } | ||
155 | |||
156 | return string(target) | ||
157 | } | ||
158 | |||
159 | // Transform characters with accents into plain forms. | ||
160 | func NeuterAccents(s string) string { | ||
161 | t := transform.Chain(norm.NFD, transform.RemoveFunc(isMn), norm.NFC) | ||
162 | result, _, _ := transform.String(t, string(s)) | ||
163 | |||
164 | return result | ||
165 | } | ||
166 | |||
167 | func isMn(r rune) bool { | ||
168 | return unicode.Is(unicode.Mn, r) // Mn: nonspacing marks | ||
169 | } | ||
170 | |||
171 | func (a Afero) FileContainsBytes(filename string, subslice []byte) (bool, error) { | ||
172 | return FileContainsBytes(a.Fs, filename, subslice) | ||
173 | } | ||
174 | |||
175 | // Check if a file contains a specified byte slice. | ||
176 | func FileContainsBytes(fs Fs, filename string, subslice []byte) (bool, error) { | ||
177 | f, err := fs.Open(filename) | ||
178 | if err != nil { | ||
179 | return false, err | ||
180 | } | ||
181 | defer f.Close() | ||
182 | |||
183 | return readerContainsAny(f, subslice), nil | ||
184 | } | ||
185 | |||
186 | func (a Afero) FileContainsAnyBytes(filename string, subslices [][]byte) (bool, error) { | ||
187 | return FileContainsAnyBytes(a.Fs, filename, subslices) | ||
188 | } | ||
189 | |||
190 | // Check if a file contains any of the specified byte slices. | ||
191 | func FileContainsAnyBytes(fs Fs, filename string, subslices [][]byte) (bool, error) { | ||
192 | f, err := fs.Open(filename) | ||
193 | if err != nil { | ||
194 | return false, err | ||
195 | } | ||
196 | defer f.Close() | ||
197 | |||
198 | return readerContainsAny(f, subslices...), nil | ||
199 | } | ||
200 | |||
201 | // readerContains reports whether any of the subslices is within r. | ||
202 | func readerContainsAny(r io.Reader, subslices ...[]byte) bool { | ||
203 | |||
204 | if r == nil || len(subslices) == 0 { | ||
205 | return false | ||
206 | } | ||
207 | |||
208 | largestSlice := 0 | ||
209 | |||
210 | for _, sl := range subslices { | ||
211 | if len(sl) > largestSlice { | ||
212 | largestSlice = len(sl) | ||
213 | } | ||
214 | } | ||
215 | |||
216 | if largestSlice == 0 { | ||
217 | return false | ||
218 | } | ||
219 | |||
220 | bufflen := largestSlice * 4 | ||
221 | halflen := bufflen / 2 | ||
222 | buff := make([]byte, bufflen) | ||
223 | var err error | ||
224 | var n, i int | ||
225 | |||
226 | for { | ||
227 | i++ | ||
228 | if i == 1 { | ||
229 | n, err = io.ReadAtLeast(r, buff[:halflen], halflen) | ||
230 | } else { | ||
231 | if i != 2 { | ||
232 | // shift left to catch overlapping matches | ||
233 | copy(buff[:], buff[halflen:]) | ||
234 | } | ||
235 | n, err = io.ReadAtLeast(r, buff[halflen:], halflen) | ||
236 | } | ||
237 | |||
238 | if n > 0 { | ||
239 | for _, sl := range subslices { | ||
240 | if bytes.Contains(buff, sl) { | ||
241 | return true | ||
242 | } | ||
243 | } | ||
244 | } | ||
245 | |||
246 | if err != nil { | ||
247 | break | ||
248 | } | ||
249 | } | ||
250 | return false | ||
251 | } | ||
252 | |||
253 | func (a Afero) DirExists(path string) (bool, error) { | ||
254 | return DirExists(a.Fs, path) | ||
255 | } | ||
256 | |||
257 | // DirExists checks if a path exists and is a directory. | ||
258 | func DirExists(fs Fs, path string) (bool, error) { | ||
259 | fi, err := fs.Stat(path) | ||
260 | if err == nil && fi.IsDir() { | ||
261 | return true, nil | ||
262 | } | ||
263 | if os.IsNotExist(err) { | ||
264 | return false, nil | ||
265 | } | ||
266 | return false, err | ||
267 | } | ||
268 | |||
269 | func (a Afero) IsDir(path string) (bool, error) { | ||
270 | return IsDir(a.Fs, path) | ||
271 | } | ||
272 | |||
273 | // IsDir checks if a given path is a directory. | ||
274 | func IsDir(fs Fs, path string) (bool, error) { | ||
275 | fi, err := fs.Stat(path) | ||
276 | if err != nil { | ||
277 | return false, err | ||
278 | } | ||
279 | return fi.IsDir(), nil | ||
280 | } | ||
281 | |||
282 | func (a Afero) IsEmpty(path string) (bool, error) { | ||
283 | return IsEmpty(a.Fs, path) | ||
284 | } | ||
285 | |||
286 | // IsEmpty checks if a given file or directory is empty. | ||
287 | func IsEmpty(fs Fs, path string) (bool, error) { | ||
288 | if b, _ := Exists(fs, path); !b { | ||
289 | return false, fmt.Errorf("%q path does not exist", path) | ||
290 | } | ||
291 | fi, err := fs.Stat(path) | ||
292 | if err != nil { | ||
293 | return false, err | ||
294 | } | ||
295 | if fi.IsDir() { | ||
296 | f, err := fs.Open(path) | ||
297 | if err != nil { | ||
298 | return false, err | ||
299 | } | ||
300 | defer f.Close() | ||
301 | list, err := f.Readdir(-1) | ||
302 | return len(list) == 0, nil | ||
303 | } | ||
304 | return fi.Size() == 0, nil | ||
305 | } | ||
306 | |||
307 | func (a Afero) Exists(path string) (bool, error) { | ||
308 | return Exists(a.Fs, path) | ||
309 | } | ||
310 | |||
311 | // Check if a file or directory exists. | ||
312 | func Exists(fs Fs, path string) (bool, error) { | ||
313 | _, err := fs.Stat(path) | ||
314 | if err == nil { | ||
315 | return true, nil | ||
316 | } | ||
317 | if os.IsNotExist(err) { | ||
318 | return false, nil | ||
319 | } | ||
320 | return false, err | ||
321 | } | ||
322 | |||
323 | func FullBaseFsPath(basePathFs *BasePathFs, relativePath string) string { | ||
324 | combinedPath := filepath.Join(basePathFs.path, relativePath) | ||
325 | if parent, ok := basePathFs.source.(*BasePathFs); ok { | ||
326 | return FullBaseFsPath(parent, combinedPath) | ||
327 | } | ||
328 | |||
329 | return combinedPath | ||
330 | } | ||