mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-21 19:01:49 +00:00
remove fakefs to drop spf13/afero dependency
Signed-off-by: Davanum Srinivas <davanum@gmail.com>
This commit is contained in:
parent
7b24c7e4a7
commit
5feff280e1
178
LICENSES/vendor/github.com/spf13/afero/LICENSE
generated
vendored
178
LICENSES/vendor/github.com/spf13/afero/LICENSE
generated
vendored
@ -1,178 +0,0 @@
|
|||||||
= vendor/github.com/spf13/afero licensed under: =
|
|
||||||
|
|
||||||
Apache License
|
|
||||||
Version 2.0, January 2004
|
|
||||||
http://www.apache.org/licenses/
|
|
||||||
|
|
||||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
||||||
|
|
||||||
1. Definitions.
|
|
||||||
|
|
||||||
"License" shall mean the terms and conditions for use, reproduction,
|
|
||||||
and distribution as defined by Sections 1 through 9 of this document.
|
|
||||||
|
|
||||||
"Licensor" shall mean the copyright owner or entity authorized by
|
|
||||||
the copyright owner that is granting the License.
|
|
||||||
|
|
||||||
"Legal Entity" shall mean the union of the acting entity and all
|
|
||||||
other entities that control, are controlled by, or are under common
|
|
||||||
control with that entity. For the purposes of this definition,
|
|
||||||
"control" means (i) the power, direct or indirect, to cause the
|
|
||||||
direction or management of such entity, whether by contract or
|
|
||||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
||||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
||||||
|
|
||||||
"You" (or "Your") shall mean an individual or Legal Entity
|
|
||||||
exercising permissions granted by this License.
|
|
||||||
|
|
||||||
"Source" form shall mean the preferred form for making modifications,
|
|
||||||
including but not limited to software source code, documentation
|
|
||||||
source, and configuration files.
|
|
||||||
|
|
||||||
"Object" form shall mean any form resulting from mechanical
|
|
||||||
transformation or translation of a Source form, including but
|
|
||||||
not limited to compiled object code, generated documentation,
|
|
||||||
and conversions to other media types.
|
|
||||||
|
|
||||||
"Work" shall mean the work of authorship, whether in Source or
|
|
||||||
Object form, made available under the License, as indicated by a
|
|
||||||
copyright notice that is included in or attached to the work
|
|
||||||
(an example is provided in the Appendix below).
|
|
||||||
|
|
||||||
"Derivative Works" shall mean any work, whether in Source or Object
|
|
||||||
form, that is based on (or derived from) the Work and for which the
|
|
||||||
editorial revisions, annotations, elaborations, or other modifications
|
|
||||||
represent, as a whole, an original work of authorship. For the purposes
|
|
||||||
of this License, Derivative Works shall not include works that remain
|
|
||||||
separable from, or merely link (or bind by name) to the interfaces of,
|
|
||||||
the Work and Derivative Works thereof.
|
|
||||||
|
|
||||||
"Contribution" shall mean any work of authorship, including
|
|
||||||
the original version of the Work and any modifications or additions
|
|
||||||
to that Work or Derivative Works thereof, that is intentionally
|
|
||||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
||||||
or by an individual or Legal Entity authorized to submit on behalf of
|
|
||||||
the copyright owner. For the purposes of this definition, "submitted"
|
|
||||||
means any form of electronic, verbal, or written communication sent
|
|
||||||
to the Licensor or its representatives, including but not limited to
|
|
||||||
communication on electronic mailing lists, source code control systems,
|
|
||||||
and issue tracking systems that are managed by, or on behalf of, the
|
|
||||||
Licensor for the purpose of discussing and improving the Work, but
|
|
||||||
excluding communication that is conspicuously marked or otherwise
|
|
||||||
designated in writing by the copyright owner as "Not a Contribution."
|
|
||||||
|
|
||||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
||||||
on behalf of whom a Contribution has been received by Licensor and
|
|
||||||
subsequently incorporated within the Work.
|
|
||||||
|
|
||||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
copyright license to reproduce, prepare Derivative Works of,
|
|
||||||
publicly display, publicly perform, sublicense, and distribute the
|
|
||||||
Work and such Derivative Works in Source or Object form.
|
|
||||||
|
|
||||||
3. Grant of Patent License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
(except as stated in this section) patent license to make, have made,
|
|
||||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
||||||
where such license applies only to those patent claims licensable
|
|
||||||
by such Contributor that are necessarily infringed by their
|
|
||||||
Contribution(s) alone or by combination of their Contribution(s)
|
|
||||||
with the Work to which such Contribution(s) was submitted. If You
|
|
||||||
institute patent litigation against any entity (including a
|
|
||||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
||||||
or a Contribution incorporated within the Work constitutes direct
|
|
||||||
or contributory patent infringement, then any patent licenses
|
|
||||||
granted to You under this License for that Work shall terminate
|
|
||||||
as of the date such litigation is filed.
|
|
||||||
|
|
||||||
4. Redistribution. You may reproduce and distribute copies of the
|
|
||||||
Work or Derivative Works thereof in any medium, with or without
|
|
||||||
modifications, and in Source or Object form, provided that You
|
|
||||||
meet the following conditions:
|
|
||||||
|
|
||||||
(a) You must give any other recipients of the Work or
|
|
||||||
Derivative Works a copy of this License; and
|
|
||||||
|
|
||||||
(b) You must cause any modified files to carry prominent notices
|
|
||||||
stating that You changed the files; and
|
|
||||||
|
|
||||||
(c) You must retain, in the Source form of any Derivative Works
|
|
||||||
that You distribute, all copyright, patent, trademark, and
|
|
||||||
attribution notices from the Source form of the Work,
|
|
||||||
excluding those notices that do not pertain to any part of
|
|
||||||
the Derivative Works; and
|
|
||||||
|
|
||||||
(d) If the Work includes a "NOTICE" text file as part of its
|
|
||||||
distribution, then any Derivative Works that You distribute must
|
|
||||||
include a readable copy of the attribution notices contained
|
|
||||||
within such NOTICE file, excluding those notices that do not
|
|
||||||
pertain to any part of the Derivative Works, in at least one
|
|
||||||
of the following places: within a NOTICE text file distributed
|
|
||||||
as part of the Derivative Works; within the Source form or
|
|
||||||
documentation, if provided along with the Derivative Works; or,
|
|
||||||
within a display generated by the Derivative Works, if and
|
|
||||||
wherever such third-party notices normally appear. The contents
|
|
||||||
of the NOTICE file are for informational purposes only and
|
|
||||||
do not modify the License. You may add Your own attribution
|
|
||||||
notices within Derivative Works that You distribute, alongside
|
|
||||||
or as an addendum to the NOTICE text from the Work, provided
|
|
||||||
that such additional attribution notices cannot be construed
|
|
||||||
as modifying the License.
|
|
||||||
|
|
||||||
You may add Your own copyright statement to Your modifications and
|
|
||||||
may provide additional or different license terms and conditions
|
|
||||||
for use, reproduction, or distribution of Your modifications, or
|
|
||||||
for any such Derivative Works as a whole, provided Your use,
|
|
||||||
reproduction, and distribution of the Work otherwise complies with
|
|
||||||
the conditions stated in this License.
|
|
||||||
|
|
||||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
||||||
any Contribution intentionally submitted for inclusion in the Work
|
|
||||||
by You to the Licensor shall be under the terms and conditions of
|
|
||||||
this License, without any additional terms or conditions.
|
|
||||||
Notwithstanding the above, nothing herein shall supersede or modify
|
|
||||||
the terms of any separate license agreement you may have executed
|
|
||||||
with Licensor regarding such Contributions.
|
|
||||||
|
|
||||||
6. Trademarks. This License does not grant permission to use the trade
|
|
||||||
names, trademarks, service marks, or product names of the Licensor,
|
|
||||||
except as required for reasonable and customary use in describing the
|
|
||||||
origin of the Work and reproducing the content of the NOTICE file.
|
|
||||||
|
|
||||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
||||||
agreed to in writing, Licensor provides the Work (and each
|
|
||||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
implied, including, without limitation, any warranties or conditions
|
|
||||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
||||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
||||||
appropriateness of using or redistributing the Work and assume any
|
|
||||||
risks associated with Your exercise of permissions under this License.
|
|
||||||
|
|
||||||
8. Limitation of Liability. In no event and under no legal theory,
|
|
||||||
whether in tort (including negligence), contract, or otherwise,
|
|
||||||
unless required by applicable law (such as deliberate and grossly
|
|
||||||
negligent acts) or agreed to in writing, shall any Contributor be
|
|
||||||
liable to You for damages, including any direct, indirect, special,
|
|
||||||
incidental, or consequential damages of any character arising as a
|
|
||||||
result of this License or out of the use or inability to use the
|
|
||||||
Work (including but not limited to damages for loss of goodwill,
|
|
||||||
work stoppage, computer failure or malfunction, or any and all
|
|
||||||
other commercial damages or losses), even if such Contributor
|
|
||||||
has been advised of the possibility of such damages.
|
|
||||||
|
|
||||||
9. Accepting Warranty or Additional Liability. While redistributing
|
|
||||||
the Work or Derivative Works thereof, You may choose to offer,
|
|
||||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
||||||
or other liability obligations and/or rights consistent with this
|
|
||||||
License. However, in accepting such obligations, You may act only
|
|
||||||
on Your own behalf and on Your sole responsibility, not on behalf
|
|
||||||
of any other Contributor, and only if You agree to indemnify,
|
|
||||||
defend, and hold each Contributor harmless for any liability
|
|
||||||
incurred by, or claims asserted against, such Contributor by reason
|
|
||||||
of your accepting any such warranty or additional liability.
|
|
||||||
|
|
||||||
= vendor/github.com/spf13/afero/LICENSE.txt 920d76114a32b0fb75b3f2718c5a91be
|
|
@ -372,7 +372,7 @@ func loadConfigFile(name string) (*kubeletconfiginternal.KubeletConfiguration, e
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf(errFmt, name, err)
|
return nil, fmt.Errorf(errFmt, name, err)
|
||||||
}
|
}
|
||||||
loader, err := configfiles.NewFsLoader(utilfs.DefaultFs{}, kubeletConfigFile)
|
loader, err := configfiles.NewFsLoader(&utilfs.DefaultFs{}, kubeletConfigFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf(errFmt, name, err)
|
return nil, fmt.Errorf(errFmt, name, err)
|
||||||
}
|
}
|
||||||
|
1
go.mod
1
go.mod
@ -77,7 +77,6 @@ require (
|
|||||||
github.com/prometheus/common v0.26.0
|
github.com/prometheus/common v0.26.0
|
||||||
github.com/quobyte/api v0.1.8
|
github.com/quobyte/api v0.1.8
|
||||||
github.com/robfig/cron/v3 v3.0.1
|
github.com/robfig/cron/v3 v3.0.1
|
||||||
github.com/spf13/afero v1.2.2
|
|
||||||
github.com/spf13/cobra v1.1.3
|
github.com/spf13/cobra v1.1.3
|
||||||
github.com/spf13/pflag v1.0.5
|
github.com/spf13/pflag v1.0.5
|
||||||
github.com/storageos/go-api v2.2.0+incompatible
|
github.com/storageos/go-api v2.2.0+incompatible
|
||||||
|
@ -54,7 +54,7 @@ type impl struct {
|
|||||||
|
|
||||||
// NewCheckpointManager returns a new instance of a checkpoint manager
|
// NewCheckpointManager returns a new instance of a checkpoint manager
|
||||||
func NewCheckpointManager(checkpointDir string) (CheckpointManager, error) {
|
func NewCheckpointManager(checkpointDir string) (CheckpointManager, error) {
|
||||||
fstore, err := utilstore.NewFileStore(checkpointDir, utilfs.DefaultFs{})
|
fstore, err := utilstore.NewFileStore(checkpointDir, &utilfs.DefaultFs{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ import (
|
|||||||
func newInitializedFakeFsStore() (*fsStore, error) {
|
func newInitializedFakeFsStore() (*fsStore, error) {
|
||||||
// Test with the default filesystem, the fake filesystem has an issue caused by afero: https://github.com/spf13/afero/issues/141
|
// Test with the default filesystem, the fake filesystem has an issue caused by afero: https://github.com/spf13/afero/issues/141
|
||||||
// The default filesystem also behaves more like production, so we should probably not mock the filesystem for unit tests.
|
// The default filesystem also behaves more like production, so we should probably not mock the filesystem for unit tests.
|
||||||
fs := utilfs.DefaultFs{}
|
fs := &utilfs.DefaultFs{}
|
||||||
|
|
||||||
tmpDir, err := fs.TempDir("", "fsstore-test-")
|
tmpDir, err := fs.TempDir("", "fsstore-test-")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -152,7 +152,8 @@ foo: bar`),
|
|||||||
|
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
t.Run(c.desc, func(t *testing.T) {
|
t.Run(c.desc, func(t *testing.T) {
|
||||||
fs := utilfs.NewFakeFs()
|
fs := utilfs.NewTempFs()
|
||||||
|
fs.MkdirAll(configDir, 0777)
|
||||||
path := filepath.Join(configDir, kubeletFile)
|
path := filepath.Join(configDir, kubeletFile)
|
||||||
if c.file != nil {
|
if c.file != nil {
|
||||||
if err := addFile(fs, path, *c.file); err != nil {
|
if err := addFile(fs, path, *c.file); err != nil {
|
||||||
|
@ -90,7 +90,7 @@ func NewController(dynamicConfigDir string, transform TransformFunc) *Controller
|
|||||||
// channels must have capacity at least 1, since we signal with non-blocking writes
|
// channels must have capacity at least 1, since we signal with non-blocking writes
|
||||||
pendingConfigSource: make(chan bool, 1),
|
pendingConfigSource: make(chan bool, 1),
|
||||||
configStatus: status.NewNodeConfigStatus(),
|
configStatus: status.NewNodeConfigStatus(),
|
||||||
checkpointStore: store.NewFsStore(utilfs.DefaultFs{}, filepath.Join(dynamicConfigDir, storeDir)),
|
checkpointStore: store.NewFsStore(&utilfs.DefaultFs{}, filepath.Join(dynamicConfigDir, storeDir)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -205,7 +205,7 @@ func TestHelpers(t *testing.T) {
|
|||||||
}
|
}
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
t.Run(c.desc, func(t *testing.T) {
|
t.Run(c.desc, func(t *testing.T) {
|
||||||
c.run(t, utilfs.DefaultFs{})
|
c.run(t, &utilfs.DefaultFs{})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -241,7 +241,7 @@ func TestFileExists(t *testing.T) {
|
|||||||
}
|
}
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
t.Run(c.desc, func(t *testing.T) {
|
t.Run(c.desc, func(t *testing.T) {
|
||||||
c.run(t, utilfs.DefaultFs{})
|
c.run(t, &utilfs.DefaultFs{})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -276,7 +276,7 @@ func TestEnsureFile(t *testing.T) {
|
|||||||
}
|
}
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
t.Run(c.desc, func(t *testing.T) {
|
t.Run(c.desc, func(t *testing.T) {
|
||||||
c.run(t, utilfs.DefaultFs{})
|
c.run(t, &utilfs.DefaultFs{})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -317,7 +317,7 @@ func TestReplaceFile(t *testing.T) {
|
|||||||
}
|
}
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
t.Run(c.desc, func(t *testing.T) {
|
t.Run(c.desc, func(t *testing.T) {
|
||||||
c.run(t, utilfs.DefaultFs{})
|
c.run(t, &utilfs.DefaultFs{})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -353,7 +353,7 @@ func TestDirExists(t *testing.T) {
|
|||||||
}
|
}
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
t.Run(c.desc, func(t *testing.T) {
|
t.Run(c.desc, func(t *testing.T) {
|
||||||
c.run(t, utilfs.DefaultFs{})
|
c.run(t, &utilfs.DefaultFs{})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -388,7 +388,7 @@ func TestEnsureDir(t *testing.T) {
|
|||||||
}
|
}
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
t.Run(c.desc, func(t *testing.T) {
|
t.Run(c.desc, func(t *testing.T) {
|
||||||
c.run(t, utilfs.DefaultFs{})
|
c.run(t, &utilfs.DefaultFs{})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -405,7 +405,7 @@ func TestWriteTempDir(t *testing.T) {
|
|||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
c.run(t, utilfs.DefaultFs{})
|
c.run(t, &utilfs.DefaultFs{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestReplaceDir(t *testing.T) {
|
func TestReplaceDir(t *testing.T) {
|
||||||
@ -471,7 +471,7 @@ func TestReplaceDir(t *testing.T) {
|
|||||||
}
|
}
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
t.Run(c.desc, func(t *testing.T) {
|
t.Run(c.desc, func(t *testing.T) {
|
||||||
c.run(t, utilfs.DefaultFs{})
|
c.run(t, &utilfs.DefaultFs{})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,8 +17,6 @@ limitations under the License.
|
|||||||
package store
|
package store
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"sort"
|
"sort"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@ -28,16 +26,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestFileStore(t *testing.T) {
|
func TestFileStore(t *testing.T) {
|
||||||
path, err := ioutil.TempDir("", "FileStore")
|
store, err := NewFileStore("/FileStore", filesystem.NewTempFs())
|
||||||
assert.NoError(t, err)
|
|
||||||
defer os.RemoveAll(path)
|
|
||||||
store, err := NewFileStore(path, filesystem.DefaultFs{})
|
|
||||||
assert.NoError(t, err)
|
|
||||||
testStore(t, store)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFakeFileStore(t *testing.T) {
|
|
||||||
store, err := NewFileStore("/tmp/test-fake-file-store", filesystem.NewFakeFs())
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
testStore(t, store)
|
testStore(t, store)
|
||||||
}
|
}
|
||||||
|
@ -20,22 +20,43 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DefaultFs implements Filesystem using same-named functions from "os" and "io/ioutil"
|
// DefaultFs implements Filesystem using same-named functions from "os" and "io/ioutil"
|
||||||
type DefaultFs struct{}
|
type DefaultFs struct {
|
||||||
|
root string
|
||||||
|
}
|
||||||
|
|
||||||
var _ Filesystem = DefaultFs{}
|
var _ Filesystem = &DefaultFs{}
|
||||||
|
|
||||||
|
// NewTempFs returns a fake Filesystem in temporary directory, useful for unit tests
|
||||||
|
func NewTempFs() Filesystem {
|
||||||
|
path, _ := ioutil.TempDir(
|
||||||
|
"",
|
||||||
|
"tmpfs",
|
||||||
|
)
|
||||||
|
return &DefaultFs{
|
||||||
|
root: path,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fs *DefaultFs) prefix(path string) string {
|
||||||
|
if len(fs.root) == 0 {
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
return filepath.Join(fs.root, path)
|
||||||
|
}
|
||||||
|
|
||||||
// Stat via os.Stat
|
// Stat via os.Stat
|
||||||
func (DefaultFs) Stat(name string) (os.FileInfo, error) {
|
func (fs *DefaultFs) Stat(name string) (os.FileInfo, error) {
|
||||||
return os.Stat(name)
|
return os.Stat(fs.prefix(name))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create via os.Create
|
// Create via os.Create
|
||||||
func (DefaultFs) Create(name string) (File, error) {
|
func (fs *DefaultFs) Create(name string) (File, error) {
|
||||||
file, err := os.Create(name)
|
file, err := os.Create(fs.prefix(name))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -43,43 +64,49 @@ func (DefaultFs) Create(name string) (File, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Rename via os.Rename
|
// Rename via os.Rename
|
||||||
func (DefaultFs) Rename(oldpath, newpath string) error {
|
func (fs *DefaultFs) Rename(oldpath, newpath string) error {
|
||||||
|
if !strings.HasPrefix(oldpath, fs.root) {
|
||||||
|
oldpath = fs.prefix(oldpath)
|
||||||
|
}
|
||||||
|
if !strings.HasPrefix(newpath, fs.root) {
|
||||||
|
newpath = fs.prefix(newpath)
|
||||||
|
}
|
||||||
return os.Rename(oldpath, newpath)
|
return os.Rename(oldpath, newpath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MkdirAll via os.MkdirAll
|
// MkdirAll via os.MkdirAll
|
||||||
func (DefaultFs) MkdirAll(path string, perm os.FileMode) error {
|
func (fs *DefaultFs) MkdirAll(path string, perm os.FileMode) error {
|
||||||
return os.MkdirAll(path, perm)
|
return os.MkdirAll(fs.prefix(path), perm)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Chtimes via os.Chtimes
|
// Chtimes via os.Chtimes
|
||||||
func (DefaultFs) Chtimes(name string, atime time.Time, mtime time.Time) error {
|
func (fs *DefaultFs) Chtimes(name string, atime time.Time, mtime time.Time) error {
|
||||||
return os.Chtimes(name, atime, mtime)
|
return os.Chtimes(fs.prefix(name), atime, mtime)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveAll via os.RemoveAll
|
// RemoveAll via os.RemoveAll
|
||||||
func (DefaultFs) RemoveAll(path string) error {
|
func (fs *DefaultFs) RemoveAll(path string) error {
|
||||||
return os.RemoveAll(path)
|
return os.RemoveAll(fs.prefix(path))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove via os.RemoveAll
|
// Remove via os.RemoveAll
|
||||||
func (DefaultFs) Remove(name string) error {
|
func (fs *DefaultFs) Remove(name string) error {
|
||||||
return os.Remove(name)
|
return os.Remove(fs.prefix(name))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadFile via ioutil.ReadFile
|
// ReadFile via ioutil.ReadFile
|
||||||
func (DefaultFs) ReadFile(filename string) ([]byte, error) {
|
func (fs *DefaultFs) ReadFile(filename string) ([]byte, error) {
|
||||||
return ioutil.ReadFile(filename)
|
return ioutil.ReadFile(fs.prefix(filename))
|
||||||
}
|
}
|
||||||
|
|
||||||
// TempDir via ioutil.TempDir
|
// TempDir via ioutil.TempDir
|
||||||
func (DefaultFs) TempDir(dir, prefix string) (string, error) {
|
func (fs *DefaultFs) TempDir(dir, prefix string) (string, error) {
|
||||||
return ioutil.TempDir(dir, prefix)
|
return ioutil.TempDir(fs.prefix(dir), prefix)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TempFile via ioutil.TempFile
|
// TempFile via ioutil.TempFile
|
||||||
func (DefaultFs) TempFile(dir, prefix string) (File, error) {
|
func (fs *DefaultFs) TempFile(dir, prefix string) (File, error) {
|
||||||
file, err := ioutil.TempFile(dir, prefix)
|
file, err := ioutil.TempFile(fs.prefix(dir), prefix)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -87,13 +114,13 @@ func (DefaultFs) TempFile(dir, prefix string) (File, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ReadDir via ioutil.ReadDir
|
// ReadDir via ioutil.ReadDir
|
||||||
func (DefaultFs) ReadDir(dirname string) ([]os.FileInfo, error) {
|
func (fs *DefaultFs) ReadDir(dirname string) ([]os.FileInfo, error) {
|
||||||
return ioutil.ReadDir(dirname)
|
return ioutil.ReadDir(fs.prefix(dirname))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Walk via filepath.Walk
|
// Walk via filepath.Walk
|
||||||
func (DefaultFs) Walk(root string, walkFn filepath.WalkFunc) error {
|
func (fs *DefaultFs) Walk(root string, walkFn filepath.WalkFunc) error {
|
||||||
return filepath.Walk(root, walkFn)
|
return filepath.Walk(fs.prefix(root), walkFn)
|
||||||
}
|
}
|
||||||
|
|
||||||
// defaultFile implements File using same-named functions from "os"
|
// defaultFile implements File using same-named functions from "os"
|
||||||
|
@ -1,128 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2017 The Kubernetes Authors.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package filesystem
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/spf13/afero"
|
|
||||||
)
|
|
||||||
|
|
||||||
// fakeFs is implemented in terms of afero
|
|
||||||
type fakeFs struct {
|
|
||||||
a afero.Afero
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewFakeFs returns a fake Filesystem that exists in-memory, useful for unit tests
|
|
||||||
func NewFakeFs() Filesystem {
|
|
||||||
return &fakeFs{a: afero.Afero{Fs: afero.NewMemMapFs()}}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stat via afero.Fs.Stat
|
|
||||||
func (fs *fakeFs) Stat(name string) (os.FileInfo, error) {
|
|
||||||
return fs.a.Fs.Stat(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create via afero.Fs.Create
|
|
||||||
func (fs *fakeFs) Create(name string) (File, error) {
|
|
||||||
file, err := fs.a.Fs.Create(name)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &fakeFile{file}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rename via afero.Fs.Rename
|
|
||||||
func (fs *fakeFs) Rename(oldpath, newpath string) error {
|
|
||||||
return fs.a.Fs.Rename(oldpath, newpath)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MkdirAll via afero.Fs.MkdirAll
|
|
||||||
func (fs *fakeFs) MkdirAll(path string, perm os.FileMode) error {
|
|
||||||
return fs.a.Fs.MkdirAll(path, perm)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Chtimes via afero.Fs.Chtimes
|
|
||||||
func (fs *fakeFs) Chtimes(name string, atime time.Time, mtime time.Time) error {
|
|
||||||
return fs.a.Fs.Chtimes(name, atime, mtime)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadFile via afero.ReadFile
|
|
||||||
func (fs *fakeFs) ReadFile(filename string) ([]byte, error) {
|
|
||||||
return fs.a.ReadFile(filename)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TempDir via afero.TempDir
|
|
||||||
func (fs *fakeFs) TempDir(dir, prefix string) (string, error) {
|
|
||||||
return fs.a.TempDir(dir, prefix)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TempFile via afero.TempFile
|
|
||||||
func (fs *fakeFs) TempFile(dir, prefix string) (File, error) {
|
|
||||||
file, err := fs.a.TempFile(dir, prefix)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &fakeFile{file}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadDir via afero.ReadDir
|
|
||||||
func (fs *fakeFs) ReadDir(dirname string) ([]os.FileInfo, error) {
|
|
||||||
return fs.a.ReadDir(dirname)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Walk via afero.Walk
|
|
||||||
func (fs *fakeFs) Walk(root string, walkFn filepath.WalkFunc) error {
|
|
||||||
return fs.a.Walk(root, walkFn)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveAll via afero.RemoveAll
|
|
||||||
func (fs *fakeFs) RemoveAll(path string) error {
|
|
||||||
return fs.a.RemoveAll(path)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove via afero.RemoveAll
|
|
||||||
func (fs *fakeFs) Remove(name string) error {
|
|
||||||
return fs.a.Remove(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// fakeFile implements File; for use with fakeFs
|
|
||||||
type fakeFile struct {
|
|
||||||
file afero.File
|
|
||||||
}
|
|
||||||
|
|
||||||
// Name via afero.File.Name
|
|
||||||
func (file *fakeFile) Name() string {
|
|
||||||
return file.file.Name()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write via afero.File.Write
|
|
||||||
func (file *fakeFile) Write(b []byte) (n int, err error) {
|
|
||||||
return file.file.Write(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sync via afero.File.Sync
|
|
||||||
func (file *fakeFile) Sync() error {
|
|
||||||
return file.file.Sync()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close via afero.File.Close
|
|
||||||
func (file *fakeFile) Close() error {
|
|
||||||
return file.file.Close()
|
|
||||||
}
|
|
@ -19,6 +19,7 @@ package flexvolume
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/fsnotify/fsnotify"
|
"github.com/fsnotify/fsnotify"
|
||||||
@ -33,6 +34,10 @@ const (
|
|||||||
driverName = "fake-driver"
|
driverName = "fake-driver"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func assertPathSuffix(t *testing.T, dir1 string, dir2 string) {
|
||||||
|
assert.True(t, strings.HasSuffix(dir2, dir1))
|
||||||
|
}
|
||||||
|
|
||||||
// Probes a driver installed before prober initialization.
|
// Probes a driver installed before prober initialization.
|
||||||
func TestProberExistingDriverBeforeInit(t *testing.T) {
|
func TestProberExistingDriverBeforeInit(t *testing.T) {
|
||||||
// Arrange
|
// Arrange
|
||||||
@ -46,8 +51,8 @@ func TestProberExistingDriverBeforeInit(t *testing.T) {
|
|||||||
// current subdirectories) registered.
|
// current subdirectories) registered.
|
||||||
assert.Equal(t, 1, len(events))
|
assert.Equal(t, 1, len(events))
|
||||||
assert.Equal(t, volume.ProbeAddOrUpdate, events[0].Op)
|
assert.Equal(t, volume.ProbeAddOrUpdate, events[0].Op)
|
||||||
assert.Equal(t, pluginDir, watcher.watches[0])
|
assertPathSuffix(t, pluginDir, watcher.watches[0])
|
||||||
assert.Equal(t, driverPath, watcher.watches[1])
|
assertPathSuffix(t, driverPath, watcher.watches[1])
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// Should no longer probe.
|
// Should no longer probe.
|
||||||
@ -84,7 +89,7 @@ func TestProberAddRemoveDriver(t *testing.T) {
|
|||||||
// Assert
|
// Assert
|
||||||
assert.Equal(t, 1, len(events))
|
assert.Equal(t, 1, len(events))
|
||||||
assert.Equal(t, volume.ProbeAddOrUpdate, events[0].Op) // 1 newly added
|
assert.Equal(t, volume.ProbeAddOrUpdate, events[0].Op) // 1 newly added
|
||||||
assert.Equal(t, driverPath, watcher.watches[len(watcher.watches)-1]) // Checks most recent watch
|
assertPathSuffix(t, driverPath, watcher.watches[len(watcher.watches)-1]) // Checks most recent watch
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// Call probe again, should return 0 event.
|
// Call probe again, should return 0 event.
|
||||||
@ -174,7 +179,7 @@ func TestProberAddRemoveDriver(t *testing.T) {
|
|||||||
// Tests the behavior when no drivers exist in the plugin directory.
|
// Tests the behavior when no drivers exist in the plugin directory.
|
||||||
func TestEmptyPluginDir(t *testing.T) {
|
func TestEmptyPluginDir(t *testing.T) {
|
||||||
// Arrange
|
// Arrange
|
||||||
fs := utilfs.NewFakeFs()
|
fs := utilfs.NewTempFs()
|
||||||
watcher := newFakeWatcher()
|
watcher := newFakeWatcher()
|
||||||
prober := &flexVolumeProber{
|
prober := &flexVolumeProber{
|
||||||
pluginDir: pluginDir,
|
pluginDir: pluginDir,
|
||||||
@ -205,7 +210,7 @@ func TestRemovePluginDir(t *testing.T) {
|
|||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
assert.Equal(t, 3, len(watcher.watches)) // 2 from initial setup, 1 from new watch.
|
assert.Equal(t, 3, len(watcher.watches)) // 2 from initial setup, 1 from new watch.
|
||||||
assert.Equal(t, pluginDir, watcher.watches[len(watcher.watches)-1])
|
assertPathSuffix(t, pluginDir, watcher.watches[len(watcher.watches)-1])
|
||||||
}
|
}
|
||||||
|
|
||||||
// Issue an event to remove plugindir. New directory should still be watched.
|
// Issue an event to remove plugindir. New directory should still be watched.
|
||||||
@ -218,22 +223,22 @@ func TestNestedDriverDir(t *testing.T) {
|
|||||||
// test add testDriverName
|
// test add testDriverName
|
||||||
testDriverName := "testDriverName"
|
testDriverName := "testDriverName"
|
||||||
testDriverPath := filepath.Join(pluginDir, testDriverName)
|
testDriverPath := filepath.Join(pluginDir, testDriverName)
|
||||||
fs.MkdirAll(testDriverPath, 0666)
|
fs.MkdirAll(testDriverPath, 0777)
|
||||||
watcher.TriggerEvent(fsnotify.Create, testDriverPath)
|
watcher.TriggerEvent(fsnotify.Create, testDriverPath)
|
||||||
// Assert
|
// Assert
|
||||||
assert.Equal(t, 3, len(watcher.watches)) // 2 from initial setup, 1 from new watch.
|
assert.Equal(t, 3, len(watcher.watches)) // 2 from initial setup, 1 from new watch.
|
||||||
assert.Equal(t, testDriverPath, watcher.watches[len(watcher.watches)-1])
|
assertPathSuffix(t, testDriverPath, watcher.watches[len(watcher.watches)-1])
|
||||||
|
|
||||||
// test add nested subdir inside testDriverName
|
// test add nested subdir inside testDriverName
|
||||||
basePath := testDriverPath
|
basePath := testDriverPath
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 10; i++ {
|
||||||
subdirName := "subdirName"
|
subdirName := "subdirName"
|
||||||
subdirPath := filepath.Join(basePath, subdirName)
|
subdirPath := filepath.Join(basePath, subdirName)
|
||||||
fs.MkdirAll(subdirPath, 0666)
|
fs.MkdirAll(subdirPath, 0777)
|
||||||
watcher.TriggerEvent(fsnotify.Create, subdirPath)
|
watcher.TriggerEvent(fsnotify.Create, subdirPath)
|
||||||
// Assert
|
// Assert
|
||||||
assert.Equal(t, 4+i, len(watcher.watches)) // 3 + newly added
|
assert.Equal(t, 4+i, len(watcher.watches)) // 3 + newly added
|
||||||
assert.Equal(t, subdirPath, watcher.watches[len(watcher.watches)-1])
|
assertPathSuffix(t, subdirPath, watcher.watches[len(watcher.watches)-1])
|
||||||
basePath = subdirPath
|
basePath = subdirPath
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -268,7 +273,7 @@ func TestProberMultipleEvents(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestProberError(t *testing.T) {
|
func TestProberError(t *testing.T) {
|
||||||
fs := utilfs.NewFakeFs()
|
fs := utilfs.NewTempFs()
|
||||||
watcher := newFakeWatcher()
|
watcher := newFakeWatcher()
|
||||||
prober := &flexVolumeProber{
|
prober := &flexVolumeProber{
|
||||||
pluginDir: pluginDir,
|
pluginDir: pluginDir,
|
||||||
@ -286,7 +291,7 @@ func TestProberError(t *testing.T) {
|
|||||||
// Installs a mock driver (an empty file) in the mock fs.
|
// Installs a mock driver (an empty file) in the mock fs.
|
||||||
func installDriver(driverName string, fs utilfs.Filesystem) {
|
func installDriver(driverName string, fs utilfs.Filesystem) {
|
||||||
driverPath := filepath.Join(pluginDir, driverName)
|
driverPath := filepath.Join(pluginDir, driverName)
|
||||||
fs.MkdirAll(driverPath, 0666)
|
fs.MkdirAll(driverPath, 0777)
|
||||||
fs.Create(filepath.Join(driverPath, driverName))
|
fs.Create(filepath.Join(driverPath, driverName))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -296,7 +301,7 @@ func initTestEnvironment(t *testing.T) (
|
|||||||
fs utilfs.Filesystem,
|
fs utilfs.Filesystem,
|
||||||
watcher *fakeWatcher,
|
watcher *fakeWatcher,
|
||||||
prober volume.DynamicPluginProber) {
|
prober volume.DynamicPluginProber) {
|
||||||
fs = utilfs.NewFakeFs()
|
fs = utilfs.NewTempFs()
|
||||||
watcher = newFakeWatcher()
|
watcher = newFakeWatcher()
|
||||||
prober = &flexVolumeProber{
|
prober = &flexVolumeProber{
|
||||||
pluginDir: pluginDir,
|
pluginDir: pluginDir,
|
||||||
|
21
vendor/github.com/spf13/afero/.travis.yml
generated
vendored
21
vendor/github.com/spf13/afero/.travis.yml
generated
vendored
@ -1,21 +0,0 @@
|
|||||||
sudo: false
|
|
||||||
language: go
|
|
||||||
|
|
||||||
go:
|
|
||||||
- 1.9
|
|
||||||
- "1.10"
|
|
||||||
- tip
|
|
||||||
|
|
||||||
os:
|
|
||||||
- linux
|
|
||||||
- osx
|
|
||||||
|
|
||||||
matrix:
|
|
||||||
allow_failures:
|
|
||||||
- go: tip
|
|
||||||
fast_finish: true
|
|
||||||
|
|
||||||
script:
|
|
||||||
- go build
|
|
||||||
- go test -race -v ./...
|
|
||||||
|
|
174
vendor/github.com/spf13/afero/LICENSE.txt
generated
vendored
174
vendor/github.com/spf13/afero/LICENSE.txt
generated
vendored
@ -1,174 +0,0 @@
|
|||||||
Apache License
|
|
||||||
Version 2.0, January 2004
|
|
||||||
http://www.apache.org/licenses/
|
|
||||||
|
|
||||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
||||||
|
|
||||||
1. Definitions.
|
|
||||||
|
|
||||||
"License" shall mean the terms and conditions for use, reproduction,
|
|
||||||
and distribution as defined by Sections 1 through 9 of this document.
|
|
||||||
|
|
||||||
"Licensor" shall mean the copyright owner or entity authorized by
|
|
||||||
the copyright owner that is granting the License.
|
|
||||||
|
|
||||||
"Legal Entity" shall mean the union of the acting entity and all
|
|
||||||
other entities that control, are controlled by, or are under common
|
|
||||||
control with that entity. For the purposes of this definition,
|
|
||||||
"control" means (i) the power, direct or indirect, to cause the
|
|
||||||
direction or management of such entity, whether by contract or
|
|
||||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
||||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
||||||
|
|
||||||
"You" (or "Your") shall mean an individual or Legal Entity
|
|
||||||
exercising permissions granted by this License.
|
|
||||||
|
|
||||||
"Source" form shall mean the preferred form for making modifications,
|
|
||||||
including but not limited to software source code, documentation
|
|
||||||
source, and configuration files.
|
|
||||||
|
|
||||||
"Object" form shall mean any form resulting from mechanical
|
|
||||||
transformation or translation of a Source form, including but
|
|
||||||
not limited to compiled object code, generated documentation,
|
|
||||||
and conversions to other media types.
|
|
||||||
|
|
||||||
"Work" shall mean the work of authorship, whether in Source or
|
|
||||||
Object form, made available under the License, as indicated by a
|
|
||||||
copyright notice that is included in or attached to the work
|
|
||||||
(an example is provided in the Appendix below).
|
|
||||||
|
|
||||||
"Derivative Works" shall mean any work, whether in Source or Object
|
|
||||||
form, that is based on (or derived from) the Work and for which the
|
|
||||||
editorial revisions, annotations, elaborations, or other modifications
|
|
||||||
represent, as a whole, an original work of authorship. For the purposes
|
|
||||||
of this License, Derivative Works shall not include works that remain
|
|
||||||
separable from, or merely link (or bind by name) to the interfaces of,
|
|
||||||
the Work and Derivative Works thereof.
|
|
||||||
|
|
||||||
"Contribution" shall mean any work of authorship, including
|
|
||||||
the original version of the Work and any modifications or additions
|
|
||||||
to that Work or Derivative Works thereof, that is intentionally
|
|
||||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
||||||
or by an individual or Legal Entity authorized to submit on behalf of
|
|
||||||
the copyright owner. For the purposes of this definition, "submitted"
|
|
||||||
means any form of electronic, verbal, or written communication sent
|
|
||||||
to the Licensor or its representatives, including but not limited to
|
|
||||||
communication on electronic mailing lists, source code control systems,
|
|
||||||
and issue tracking systems that are managed by, or on behalf of, the
|
|
||||||
Licensor for the purpose of discussing and improving the Work, but
|
|
||||||
excluding communication that is conspicuously marked or otherwise
|
|
||||||
designated in writing by the copyright owner as "Not a Contribution."
|
|
||||||
|
|
||||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
||||||
on behalf of whom a Contribution has been received by Licensor and
|
|
||||||
subsequently incorporated within the Work.
|
|
||||||
|
|
||||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
copyright license to reproduce, prepare Derivative Works of,
|
|
||||||
publicly display, publicly perform, sublicense, and distribute the
|
|
||||||
Work and such Derivative Works in Source or Object form.
|
|
||||||
|
|
||||||
3. Grant of Patent License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
(except as stated in this section) patent license to make, have made,
|
|
||||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
||||||
where such license applies only to those patent claims licensable
|
|
||||||
by such Contributor that are necessarily infringed by their
|
|
||||||
Contribution(s) alone or by combination of their Contribution(s)
|
|
||||||
with the Work to which such Contribution(s) was submitted. If You
|
|
||||||
institute patent litigation against any entity (including a
|
|
||||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
||||||
or a Contribution incorporated within the Work constitutes direct
|
|
||||||
or contributory patent infringement, then any patent licenses
|
|
||||||
granted to You under this License for that Work shall terminate
|
|
||||||
as of the date such litigation is filed.
|
|
||||||
|
|
||||||
4. Redistribution. You may reproduce and distribute copies of the
|
|
||||||
Work or Derivative Works thereof in any medium, with or without
|
|
||||||
modifications, and in Source or Object form, provided that You
|
|
||||||
meet the following conditions:
|
|
||||||
|
|
||||||
(a) You must give any other recipients of the Work or
|
|
||||||
Derivative Works a copy of this License; and
|
|
||||||
|
|
||||||
(b) You must cause any modified files to carry prominent notices
|
|
||||||
stating that You changed the files; and
|
|
||||||
|
|
||||||
(c) You must retain, in the Source form of any Derivative Works
|
|
||||||
that You distribute, all copyright, patent, trademark, and
|
|
||||||
attribution notices from the Source form of the Work,
|
|
||||||
excluding those notices that do not pertain to any part of
|
|
||||||
the Derivative Works; and
|
|
||||||
|
|
||||||
(d) If the Work includes a "NOTICE" text file as part of its
|
|
||||||
distribution, then any Derivative Works that You distribute must
|
|
||||||
include a readable copy of the attribution notices contained
|
|
||||||
within such NOTICE file, excluding those notices that do not
|
|
||||||
pertain to any part of the Derivative Works, in at least one
|
|
||||||
of the following places: within a NOTICE text file distributed
|
|
||||||
as part of the Derivative Works; within the Source form or
|
|
||||||
documentation, if provided along with the Derivative Works; or,
|
|
||||||
within a display generated by the Derivative Works, if and
|
|
||||||
wherever such third-party notices normally appear. The contents
|
|
||||||
of the NOTICE file are for informational purposes only and
|
|
||||||
do not modify the License. You may add Your own attribution
|
|
||||||
notices within Derivative Works that You distribute, alongside
|
|
||||||
or as an addendum to the NOTICE text from the Work, provided
|
|
||||||
that such additional attribution notices cannot be construed
|
|
||||||
as modifying the License.
|
|
||||||
|
|
||||||
You may add Your own copyright statement to Your modifications and
|
|
||||||
may provide additional or different license terms and conditions
|
|
||||||
for use, reproduction, or distribution of Your modifications, or
|
|
||||||
for any such Derivative Works as a whole, provided Your use,
|
|
||||||
reproduction, and distribution of the Work otherwise complies with
|
|
||||||
the conditions stated in this License.
|
|
||||||
|
|
||||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
||||||
any Contribution intentionally submitted for inclusion in the Work
|
|
||||||
by You to the Licensor shall be under the terms and conditions of
|
|
||||||
this License, without any additional terms or conditions.
|
|
||||||
Notwithstanding the above, nothing herein shall supersede or modify
|
|
||||||
the terms of any separate license agreement you may have executed
|
|
||||||
with Licensor regarding such Contributions.
|
|
||||||
|
|
||||||
6. Trademarks. This License does not grant permission to use the trade
|
|
||||||
names, trademarks, service marks, or product names of the Licensor,
|
|
||||||
except as required for reasonable and customary use in describing the
|
|
||||||
origin of the Work and reproducing the content of the NOTICE file.
|
|
||||||
|
|
||||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
||||||
agreed to in writing, Licensor provides the Work (and each
|
|
||||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
implied, including, without limitation, any warranties or conditions
|
|
||||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
||||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
||||||
appropriateness of using or redistributing the Work and assume any
|
|
||||||
risks associated with Your exercise of permissions under this License.
|
|
||||||
|
|
||||||
8. Limitation of Liability. In no event and under no legal theory,
|
|
||||||
whether in tort (including negligence), contract, or otherwise,
|
|
||||||
unless required by applicable law (such as deliberate and grossly
|
|
||||||
negligent acts) or agreed to in writing, shall any Contributor be
|
|
||||||
liable to You for damages, including any direct, indirect, special,
|
|
||||||
incidental, or consequential damages of any character arising as a
|
|
||||||
result of this License or out of the use or inability to use the
|
|
||||||
Work (including but not limited to damages for loss of goodwill,
|
|
||||||
work stoppage, computer failure or malfunction, or any and all
|
|
||||||
other commercial damages or losses), even if such Contributor
|
|
||||||
has been advised of the possibility of such damages.
|
|
||||||
|
|
||||||
9. Accepting Warranty or Additional Liability. While redistributing
|
|
||||||
the Work or Derivative Works thereof, You may choose to offer,
|
|
||||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
||||||
or other liability obligations and/or rights consistent with this
|
|
||||||
License. However, in accepting such obligations, You may act only
|
|
||||||
on Your own behalf and on Your sole responsibility, not on behalf
|
|
||||||
of any other Contributor, and only if You agree to indemnify,
|
|
||||||
defend, and hold each Contributor harmless for any liability
|
|
||||||
incurred by, or claims asserted against, such Contributor by reason
|
|
||||||
of your accepting any such warranty or additional liability.
|
|
452
vendor/github.com/spf13/afero/README.md
generated
vendored
452
vendor/github.com/spf13/afero/README.md
generated
vendored
@ -1,452 +0,0 @@
|
|||||||

|
|
||||||
|
|
||||||
A FileSystem Abstraction System for Go
|
|
||||||
|
|
||||||
[](https://travis-ci.org/spf13/afero) [](https://ci.appveyor.com/project/spf13/afero) [](https://godoc.org/github.com/spf13/afero) [](https://gitter.im/spf13/afero?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
|
||||||
|
|
||||||
# Overview
|
|
||||||
|
|
||||||
Afero is an filesystem framework providing a simple, uniform and universal API
|
|
||||||
interacting with any filesystem, as an abstraction layer providing interfaces,
|
|
||||||
types and methods. Afero has an exceptionally clean interface and simple design
|
|
||||||
without needless constructors or initialization methods.
|
|
||||||
|
|
||||||
Afero is also a library providing a base set of interoperable backend
|
|
||||||
filesystems that make it easy to work with afero while retaining all the power
|
|
||||||
and benefit of the os and ioutil packages.
|
|
||||||
|
|
||||||
Afero provides significant improvements over using the os package alone, most
|
|
||||||
notably the ability to create mock and testing filesystems without relying on the disk.
|
|
||||||
|
|
||||||
It is suitable for use in a any situation where you would consider using the OS
|
|
||||||
package as it provides an additional abstraction that makes it easy to use a
|
|
||||||
memory backed file system during testing. It also adds support for the http
|
|
||||||
filesystem for full interoperability.
|
|
||||||
|
|
||||||
|
|
||||||
## Afero Features
|
|
||||||
|
|
||||||
* A single consistent API for accessing a variety of filesystems
|
|
||||||
* Interoperation between a variety of file system types
|
|
||||||
* A set of interfaces to encourage and enforce interoperability between backends
|
|
||||||
* An atomic cross platform memory backed file system
|
|
||||||
* Support for compositional (union) file systems by combining multiple file systems acting as one
|
|
||||||
* Specialized backends which modify existing filesystems (Read Only, Regexp filtered)
|
|
||||||
* A set of utility functions ported from io, ioutil & hugo to be afero aware
|
|
||||||
|
|
||||||
|
|
||||||
# Using Afero
|
|
||||||
|
|
||||||
Afero is easy to use and easier to adopt.
|
|
||||||
|
|
||||||
A few different ways you could use Afero:
|
|
||||||
|
|
||||||
* Use the interfaces alone to define you own file system.
|
|
||||||
* Wrap for the OS packages.
|
|
||||||
* Define different filesystems for different parts of your application.
|
|
||||||
* Use Afero for mock filesystems while testing
|
|
||||||
|
|
||||||
## Step 1: Install Afero
|
|
||||||
|
|
||||||
First use go get to install the latest version of the library.
|
|
||||||
|
|
||||||
$ go get github.com/spf13/afero
|
|
||||||
|
|
||||||
Next include Afero in your application.
|
|
||||||
```go
|
|
||||||
import "github.com/spf13/afero"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Step 2: Declare a backend
|
|
||||||
|
|
||||||
First define a package variable and set it to a pointer to a filesystem.
|
|
||||||
```go
|
|
||||||
var AppFs = afero.NewMemMapFs()
|
|
||||||
|
|
||||||
or
|
|
||||||
|
|
||||||
var AppFs = afero.NewOsFs()
|
|
||||||
```
|
|
||||||
It is important to note that if you repeat the composite literal you
|
|
||||||
will be using a completely new and isolated filesystem. In the case of
|
|
||||||
OsFs it will still use the same underlying filesystem but will reduce
|
|
||||||
the ability to drop in other filesystems as desired.
|
|
||||||
|
|
||||||
## Step 3: Use it like you would the OS package
|
|
||||||
|
|
||||||
Throughout your application use any function and method like you normally
|
|
||||||
would.
|
|
||||||
|
|
||||||
So if my application before had:
|
|
||||||
```go
|
|
||||||
os.Open('/tmp/foo')
|
|
||||||
```
|
|
||||||
We would replace it with:
|
|
||||||
```go
|
|
||||||
AppFs.Open('/tmp/foo')
|
|
||||||
```
|
|
||||||
|
|
||||||
`AppFs` being the variable we defined above.
|
|
||||||
|
|
||||||
|
|
||||||
## List of all available functions
|
|
||||||
|
|
||||||
File System Methods Available:
|
|
||||||
```go
|
|
||||||
Chmod(name string, mode os.FileMode) : error
|
|
||||||
Chtimes(name string, atime time.Time, mtime time.Time) : error
|
|
||||||
Create(name string) : File, error
|
|
||||||
Mkdir(name string, perm os.FileMode) : error
|
|
||||||
MkdirAll(path string, perm os.FileMode) : error
|
|
||||||
Name() : string
|
|
||||||
Open(name string) : File, error
|
|
||||||
OpenFile(name string, flag int, perm os.FileMode) : File, error
|
|
||||||
Remove(name string) : error
|
|
||||||
RemoveAll(path string) : error
|
|
||||||
Rename(oldname, newname string) : error
|
|
||||||
Stat(name string) : os.FileInfo, error
|
|
||||||
```
|
|
||||||
File Interfaces and Methods Available:
|
|
||||||
```go
|
|
||||||
io.Closer
|
|
||||||
io.Reader
|
|
||||||
io.ReaderAt
|
|
||||||
io.Seeker
|
|
||||||
io.Writer
|
|
||||||
io.WriterAt
|
|
||||||
|
|
||||||
Name() : string
|
|
||||||
Readdir(count int) : []os.FileInfo, error
|
|
||||||
Readdirnames(n int) : []string, error
|
|
||||||
Stat() : os.FileInfo, error
|
|
||||||
Sync() : error
|
|
||||||
Truncate(size int64) : error
|
|
||||||
WriteString(s string) : ret int, err error
|
|
||||||
```
|
|
||||||
In some applications it may make sense to define a new package that
|
|
||||||
simply exports the file system variable for easy access from anywhere.
|
|
||||||
|
|
||||||
## Using Afero's utility functions
|
|
||||||
|
|
||||||
Afero provides a set of functions to make it easier to use the underlying file systems.
|
|
||||||
These functions have been primarily ported from io & ioutil with some developed for Hugo.
|
|
||||||
|
|
||||||
The afero utilities support all afero compatible backends.
|
|
||||||
|
|
||||||
The list of utilities includes:
|
|
||||||
|
|
||||||
```go
|
|
||||||
DirExists(path string) (bool, error)
|
|
||||||
Exists(path string) (bool, error)
|
|
||||||
FileContainsBytes(filename string, subslice []byte) (bool, error)
|
|
||||||
GetTempDir(subPath string) string
|
|
||||||
IsDir(path string) (bool, error)
|
|
||||||
IsEmpty(path string) (bool, error)
|
|
||||||
ReadDir(dirname string) ([]os.FileInfo, error)
|
|
||||||
ReadFile(filename string) ([]byte, error)
|
|
||||||
SafeWriteReader(path string, r io.Reader) (err error)
|
|
||||||
TempDir(dir, prefix string) (name string, err error)
|
|
||||||
TempFile(dir, prefix string) (f File, err error)
|
|
||||||
Walk(root string, walkFn filepath.WalkFunc) error
|
|
||||||
WriteFile(filename string, data []byte, perm os.FileMode) error
|
|
||||||
WriteReader(path string, r io.Reader) (err error)
|
|
||||||
```
|
|
||||||
For a complete list see [Afero's GoDoc](https://godoc.org/github.com/spf13/afero)
|
|
||||||
|
|
||||||
They are available under two different approaches to use. You can either call
|
|
||||||
them directly where the first parameter of each function will be the file
|
|
||||||
system, or you can declare a new `Afero`, a custom type used to bind these
|
|
||||||
functions as methods to a given filesystem.
|
|
||||||
|
|
||||||
### Calling utilities directly
|
|
||||||
|
|
||||||
```go
|
|
||||||
fs := new(afero.MemMapFs)
|
|
||||||
f, err := afero.TempFile(fs,"", "ioutil-test")
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
### Calling via Afero
|
|
||||||
|
|
||||||
```go
|
|
||||||
fs := afero.NewMemMapFs()
|
|
||||||
afs := &afero.Afero{Fs: fs}
|
|
||||||
f, err := afs.TempFile("", "ioutil-test")
|
|
||||||
```
|
|
||||||
|
|
||||||
## Using Afero for Testing
|
|
||||||
|
|
||||||
There is a large benefit to using a mock filesystem for testing. It has a
|
|
||||||
completely blank state every time it is initialized and can be easily
|
|
||||||
reproducible regardless of OS. You could create files to your heart’s content
|
|
||||||
and the file access would be fast while also saving you from all the annoying
|
|
||||||
issues with deleting temporary files, Windows file locking, etc. The MemMapFs
|
|
||||||
backend is perfect for testing.
|
|
||||||
|
|
||||||
* Much faster than performing I/O operations on disk
|
|
||||||
* Avoid security issues and permissions
|
|
||||||
* Far more control. 'rm -rf /' with confidence
|
|
||||||
* Test setup is far more easier to do
|
|
||||||
* No test cleanup needed
|
|
||||||
|
|
||||||
One way to accomplish this is to define a variable as mentioned above.
|
|
||||||
In your application this will be set to afero.NewOsFs() during testing you
|
|
||||||
can set it to afero.NewMemMapFs().
|
|
||||||
|
|
||||||
It wouldn't be uncommon to have each test initialize a blank slate memory
|
|
||||||
backend. To do this I would define my `appFS = afero.NewOsFs()` somewhere
|
|
||||||
appropriate in my application code. This approach ensures that Tests are order
|
|
||||||
independent, with no test relying on the state left by an earlier test.
|
|
||||||
|
|
||||||
Then in my tests I would initialize a new MemMapFs for each test:
|
|
||||||
```go
|
|
||||||
func TestExist(t *testing.T) {
|
|
||||||
appFS := afero.NewMemMapFs()
|
|
||||||
// create test files and directories
|
|
||||||
appFS.MkdirAll("src/a", 0755)
|
|
||||||
afero.WriteFile(appFS, "src/a/b", []byte("file b"), 0644)
|
|
||||||
afero.WriteFile(appFS, "src/c", []byte("file c"), 0644)
|
|
||||||
name := "src/c"
|
|
||||||
_, err := appFS.Stat(name)
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
t.Errorf("file \"%s\" does not exist.\n", name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
# Available Backends
|
|
||||||
|
|
||||||
## Operating System Native
|
|
||||||
|
|
||||||
### OsFs
|
|
||||||
|
|
||||||
The first is simply a wrapper around the native OS calls. This makes it
|
|
||||||
very easy to use as all of the calls are the same as the existing OS
|
|
||||||
calls. It also makes it trivial to have your code use the OS during
|
|
||||||
operation and a mock filesystem during testing or as needed.
|
|
||||||
|
|
||||||
```go
|
|
||||||
appfs := afero.NewOsFs()
|
|
||||||
appfs.MkdirAll("src/a", 0755))
|
|
||||||
```
|
|
||||||
|
|
||||||
## Memory Backed Storage
|
|
||||||
|
|
||||||
### MemMapFs
|
|
||||||
|
|
||||||
Afero also provides a fully atomic memory backed filesystem perfect for use in
|
|
||||||
mocking and to speed up unnecessary disk io when persistence isn’t
|
|
||||||
necessary. It is fully concurrent and will work within go routines
|
|
||||||
safely.
|
|
||||||
|
|
||||||
```go
|
|
||||||
mm := afero.NewMemMapFs()
|
|
||||||
mm.MkdirAll("src/a", 0755))
|
|
||||||
```
|
|
||||||
|
|
||||||
#### InMemoryFile
|
|
||||||
|
|
||||||
As part of MemMapFs, Afero also provides an atomic, fully concurrent memory
|
|
||||||
backed file implementation. This can be used in other memory backed file
|
|
||||||
systems with ease. Plans are to add a radix tree memory stored file
|
|
||||||
system using InMemoryFile.
|
|
||||||
|
|
||||||
## Network Interfaces
|
|
||||||
|
|
||||||
### SftpFs
|
|
||||||
|
|
||||||
Afero has experimental support for secure file transfer protocol (sftp). Which can
|
|
||||||
be used to perform file operations over a encrypted channel.
|
|
||||||
|
|
||||||
## Filtering Backends
|
|
||||||
|
|
||||||
### BasePathFs
|
|
||||||
|
|
||||||
The BasePathFs restricts all operations to a given path within an Fs.
|
|
||||||
The given file name to the operations on this Fs will be prepended with
|
|
||||||
the base path before calling the source Fs.
|
|
||||||
|
|
||||||
```go
|
|
||||||
bp := afero.NewBasePathFs(afero.NewOsFs(), "/base/path")
|
|
||||||
```
|
|
||||||
|
|
||||||
### ReadOnlyFs
|
|
||||||
|
|
||||||
A thin wrapper around the source Fs providing a read only view.
|
|
||||||
|
|
||||||
```go
|
|
||||||
fs := afero.NewReadOnlyFs(afero.NewOsFs())
|
|
||||||
_, err := fs.Create("/file.txt")
|
|
||||||
// err = syscall.EPERM
|
|
||||||
```
|
|
||||||
|
|
||||||
# RegexpFs
|
|
||||||
|
|
||||||
A filtered view on file names, any file NOT matching
|
|
||||||
the passed regexp will be treated as non-existing.
|
|
||||||
Files not matching the regexp provided will not be created.
|
|
||||||
Directories are not filtered.
|
|
||||||
|
|
||||||
```go
|
|
||||||
fs := afero.NewRegexpFs(afero.NewMemMapFs(), regexp.MustCompile(`\.txt$`))
|
|
||||||
_, err := fs.Create("/file.html")
|
|
||||||
// err = syscall.ENOENT
|
|
||||||
```
|
|
||||||
|
|
||||||
### HttpFs
|
|
||||||
|
|
||||||
Afero provides an http compatible backend which can wrap any of the existing
|
|
||||||
backends.
|
|
||||||
|
|
||||||
The Http package requires a slightly specific version of Open which
|
|
||||||
returns an http.File type.
|
|
||||||
|
|
||||||
Afero provides an httpFs file system which satisfies this requirement.
|
|
||||||
Any Afero FileSystem can be used as an httpFs.
|
|
||||||
|
|
||||||
```go
|
|
||||||
httpFs := afero.NewHttpFs(<ExistingFS>)
|
|
||||||
fileserver := http.FileServer(httpFs.Dir(<PATH>)))
|
|
||||||
http.Handle("/", fileserver)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Composite Backends
|
|
||||||
|
|
||||||
Afero provides the ability have two filesystems (or more) act as a single
|
|
||||||
file system.
|
|
||||||
|
|
||||||
### CacheOnReadFs
|
|
||||||
|
|
||||||
The CacheOnReadFs will lazily make copies of any accessed files from the base
|
|
||||||
layer into the overlay. Subsequent reads will be pulled from the overlay
|
|
||||||
directly permitting the request is within the cache duration of when it was
|
|
||||||
created in the overlay.
|
|
||||||
|
|
||||||
If the base filesystem is writeable, any changes to files will be
|
|
||||||
done first to the base, then to the overlay layer. Write calls to open file
|
|
||||||
handles like `Write()` or `Truncate()` to the overlay first.
|
|
||||||
|
|
||||||
To writing files to the overlay only, you can use the overlay Fs directly (not
|
|
||||||
via the union Fs).
|
|
||||||
|
|
||||||
Cache files in the layer for the given time.Duration, a cache duration of 0
|
|
||||||
means "forever" meaning the file will not be re-requested from the base ever.
|
|
||||||
|
|
||||||
A read-only base will make the overlay also read-only but still copy files
|
|
||||||
from the base to the overlay when they're not present (or outdated) in the
|
|
||||||
caching layer.
|
|
||||||
|
|
||||||
```go
|
|
||||||
base := afero.NewOsFs()
|
|
||||||
layer := afero.NewMemMapFs()
|
|
||||||
ufs := afero.NewCacheOnReadFs(base, layer, 100 * time.Second)
|
|
||||||
```
|
|
||||||
|
|
||||||
### CopyOnWriteFs()
|
|
||||||
|
|
||||||
The CopyOnWriteFs is a read only base file system with a potentially
|
|
||||||
writeable layer on top.
|
|
||||||
|
|
||||||
Read operations will first look in the overlay and if not found there, will
|
|
||||||
serve the file from the base.
|
|
||||||
|
|
||||||
Changes to the file system will only be made in the overlay.
|
|
||||||
|
|
||||||
Any attempt to modify a file found only in the base will copy the file to the
|
|
||||||
overlay layer before modification (including opening a file with a writable
|
|
||||||
handle).
|
|
||||||
|
|
||||||
Removing and Renaming files present only in the base layer is not currently
|
|
||||||
permitted. If a file is present in the base layer and the overlay, only the
|
|
||||||
overlay will be removed/renamed.
|
|
||||||
|
|
||||||
```go
|
|
||||||
base := afero.NewOsFs()
|
|
||||||
roBase := afero.NewReadOnlyFs(base)
|
|
||||||
ufs := afero.NewCopyOnWriteFs(roBase, afero.NewMemMapFs())
|
|
||||||
|
|
||||||
fh, _ = ufs.Create("/home/test/file2.txt")
|
|
||||||
fh.WriteString("This is a test")
|
|
||||||
fh.Close()
|
|
||||||
```
|
|
||||||
|
|
||||||
In this example all write operations will only occur in memory (MemMapFs)
|
|
||||||
leaving the base filesystem (OsFs) untouched.
|
|
||||||
|
|
||||||
|
|
||||||
## Desired/possible backends
|
|
||||||
|
|
||||||
The following is a short list of possible backends we hope someone will
|
|
||||||
implement:
|
|
||||||
|
|
||||||
* SSH
|
|
||||||
* ZIP
|
|
||||||
* TAR
|
|
||||||
* S3
|
|
||||||
|
|
||||||
# About the project
|
|
||||||
|
|
||||||
## What's in the name
|
|
||||||
|
|
||||||
Afero comes from the latin roots Ad-Facere.
|
|
||||||
|
|
||||||
**"Ad"** is a prefix meaning "to".
|
|
||||||
|
|
||||||
**"Facere"** is a form of the root "faciō" making "make or do".
|
|
||||||
|
|
||||||
The literal meaning of afero is "to make" or "to do" which seems very fitting
|
|
||||||
for a library that allows one to make files and directories and do things with them.
|
|
||||||
|
|
||||||
The English word that shares the same roots as Afero is "affair". Affair shares
|
|
||||||
the same concept but as a noun it means "something that is made or done" or "an
|
|
||||||
object of a particular type".
|
|
||||||
|
|
||||||
It's also nice that unlike some of my other libraries (hugo, cobra, viper) it
|
|
||||||
Googles very well.
|
|
||||||
|
|
||||||
## Release Notes
|
|
||||||
|
|
||||||
* **0.10.0** 2015.12.10
|
|
||||||
* Full compatibility with Windows
|
|
||||||
* Introduction of afero utilities
|
|
||||||
* Test suite rewritten to work cross platform
|
|
||||||
* Normalize paths for MemMapFs
|
|
||||||
* Adding Sync to the file interface
|
|
||||||
* **Breaking Change** Walk and ReadDir have changed parameter order
|
|
||||||
* Moving types used by MemMapFs to a subpackage
|
|
||||||
* General bugfixes and improvements
|
|
||||||
* **0.9.0** 2015.11.05
|
|
||||||
* New Walk function similar to filepath.Walk
|
|
||||||
* MemMapFs.OpenFile handles O_CREATE, O_APPEND, O_TRUNC
|
|
||||||
* MemMapFs.Remove now really deletes the file
|
|
||||||
* InMemoryFile.Readdir and Readdirnames work correctly
|
|
||||||
* InMemoryFile functions lock it for concurrent access
|
|
||||||
* Test suite improvements
|
|
||||||
* **0.8.0** 2014.10.28
|
|
||||||
* First public version
|
|
||||||
* Interfaces feel ready for people to build using
|
|
||||||
* Interfaces satisfy all known uses
|
|
||||||
* MemMapFs passes the majority of the OS test suite
|
|
||||||
* OsFs passes the majority of the OS test suite
|
|
||||||
|
|
||||||
## Contributing
|
|
||||||
|
|
||||||
1. Fork it
|
|
||||||
2. Create your feature branch (`git checkout -b my-new-feature`)
|
|
||||||
3. Commit your changes (`git commit -am 'Add some feature'`)
|
|
||||||
4. Push to the branch (`git push origin my-new-feature`)
|
|
||||||
5. Create new Pull Request
|
|
||||||
|
|
||||||
## Contributors
|
|
||||||
|
|
||||||
Names in no particular order:
|
|
||||||
|
|
||||||
* [spf13](https://github.com/spf13)
|
|
||||||
* [jaqx0r](https://github.com/jaqx0r)
|
|
||||||
* [mbertschler](https://github.com/mbertschler)
|
|
||||||
* [xor-gate](https://github.com/xor-gate)
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
Afero is released under the Apache 2.0 license. See
|
|
||||||
[LICENSE.txt](https://github.com/spf13/afero/blob/master/LICENSE.txt)
|
|
108
vendor/github.com/spf13/afero/afero.go
generated
vendored
108
vendor/github.com/spf13/afero/afero.go
generated
vendored
@ -1,108 +0,0 @@
|
|||||||
// Copyright © 2014 Steve Francia <spf@spf13.com>.
|
|
||||||
// Copyright 2013 tsuru authors. All rights reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
// Package afero provides types and methods for interacting with the filesystem,
|
|
||||||
// as an abstraction layer.
|
|
||||||
|
|
||||||
// Afero also provides a few implementations that are mostly interoperable. One that
|
|
||||||
// uses the operating system filesystem, one that uses memory to store files
|
|
||||||
// (cross platform) and an interface that should be implemented if you want to
|
|
||||||
// provide your own filesystem.
|
|
||||||
|
|
||||||
package afero
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Afero struct {
|
|
||||||
Fs
|
|
||||||
}
|
|
||||||
|
|
||||||
// File represents a file in the filesystem.
|
|
||||||
type File interface {
|
|
||||||
io.Closer
|
|
||||||
io.Reader
|
|
||||||
io.ReaderAt
|
|
||||||
io.Seeker
|
|
||||||
io.Writer
|
|
||||||
io.WriterAt
|
|
||||||
|
|
||||||
Name() string
|
|
||||||
Readdir(count int) ([]os.FileInfo, error)
|
|
||||||
Readdirnames(n int) ([]string, error)
|
|
||||||
Stat() (os.FileInfo, error)
|
|
||||||
Sync() error
|
|
||||||
Truncate(size int64) error
|
|
||||||
WriteString(s string) (ret int, err error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fs is the filesystem interface.
|
|
||||||
//
|
|
||||||
// Any simulated or real filesystem should implement this interface.
|
|
||||||
type Fs interface {
|
|
||||||
// Create creates a file in the filesystem, returning the file and an
|
|
||||||
// error, if any happens.
|
|
||||||
Create(name string) (File, error)
|
|
||||||
|
|
||||||
// Mkdir creates a directory in the filesystem, return an error if any
|
|
||||||
// happens.
|
|
||||||
Mkdir(name string, perm os.FileMode) error
|
|
||||||
|
|
||||||
// MkdirAll creates a directory path and all parents that does not exist
|
|
||||||
// yet.
|
|
||||||
MkdirAll(path string, perm os.FileMode) error
|
|
||||||
|
|
||||||
// Open opens a file, returning it or an error, if any happens.
|
|
||||||
Open(name string) (File, error)
|
|
||||||
|
|
||||||
// OpenFile opens a file using the given flags and the given mode.
|
|
||||||
OpenFile(name string, flag int, perm os.FileMode) (File, error)
|
|
||||||
|
|
||||||
// Remove removes a file identified by name, returning an error, if any
|
|
||||||
// happens.
|
|
||||||
Remove(name string) error
|
|
||||||
|
|
||||||
// RemoveAll removes a directory path and any children it contains. It
|
|
||||||
// does not fail if the path does not exist (return nil).
|
|
||||||
RemoveAll(path string) error
|
|
||||||
|
|
||||||
// Rename renames a file.
|
|
||||||
Rename(oldname, newname string) error
|
|
||||||
|
|
||||||
// Stat returns a FileInfo describing the named file, or an error, if any
|
|
||||||
// happens.
|
|
||||||
Stat(name string) (os.FileInfo, error)
|
|
||||||
|
|
||||||
// The name of this FileSystem
|
|
||||||
Name() string
|
|
||||||
|
|
||||||
//Chmod changes the mode of the named file to mode.
|
|
||||||
Chmod(name string, mode os.FileMode) error
|
|
||||||
|
|
||||||
//Chtimes changes the access and modification times of the named file
|
|
||||||
Chtimes(name string, atime time.Time, mtime time.Time) error
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrFileClosed = errors.New("File is closed")
|
|
||||||
ErrOutOfRange = errors.New("Out of range")
|
|
||||||
ErrTooLarge = errors.New("Too large")
|
|
||||||
ErrFileNotFound = os.ErrNotExist
|
|
||||||
ErrFileExists = os.ErrExist
|
|
||||||
ErrDestinationExists = os.ErrExist
|
|
||||||
)
|
|
15
vendor/github.com/spf13/afero/appveyor.yml
generated
vendored
15
vendor/github.com/spf13/afero/appveyor.yml
generated
vendored
@ -1,15 +0,0 @@
|
|||||||
version: '{build}'
|
|
||||||
clone_folder: C:\gopath\src\github.com\spf13\afero
|
|
||||||
environment:
|
|
||||||
GOPATH: C:\gopath
|
|
||||||
build_script:
|
|
||||||
- cmd: >-
|
|
||||||
go version
|
|
||||||
|
|
||||||
go env
|
|
||||||
|
|
||||||
go get -v github.com/spf13/afero/...
|
|
||||||
|
|
||||||
go build github.com/spf13/afero
|
|
||||||
test_script:
|
|
||||||
- cmd: go test -race -v github.com/spf13/afero/...
|
|
180
vendor/github.com/spf13/afero/basepath.go
generated
vendored
180
vendor/github.com/spf13/afero/basepath.go
generated
vendored
@ -1,180 +0,0 @@
|
|||||||
package afero
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var _ Lstater = (*BasePathFs)(nil)
|
|
||||||
|
|
||||||
// The BasePathFs restricts all operations to a given path within an Fs.
|
|
||||||
// The given file name to the operations on this Fs will be prepended with
|
|
||||||
// the base path before calling the base Fs.
|
|
||||||
// Any file name (after filepath.Clean()) outside this base path will be
|
|
||||||
// treated as non existing file.
|
|
||||||
//
|
|
||||||
// Note that it does not clean the error messages on return, so you may
|
|
||||||
// reveal the real path on errors.
|
|
||||||
type BasePathFs struct {
|
|
||||||
source Fs
|
|
||||||
path string
|
|
||||||
}
|
|
||||||
|
|
||||||
type BasePathFile struct {
|
|
||||||
File
|
|
||||||
path string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *BasePathFile) Name() string {
|
|
||||||
sourcename := f.File.Name()
|
|
||||||
return strings.TrimPrefix(sourcename, filepath.Clean(f.path))
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewBasePathFs(source Fs, path string) Fs {
|
|
||||||
return &BasePathFs{source: source, path: path}
|
|
||||||
}
|
|
||||||
|
|
||||||
// on a file outside the base path it returns the given file name and an error,
|
|
||||||
// else the given file with the base path prepended
|
|
||||||
func (b *BasePathFs) RealPath(name string) (path string, err error) {
|
|
||||||
if err := validateBasePathName(name); err != nil {
|
|
||||||
return name, err
|
|
||||||
}
|
|
||||||
|
|
||||||
bpath := filepath.Clean(b.path)
|
|
||||||
path = filepath.Clean(filepath.Join(bpath, name))
|
|
||||||
if !strings.HasPrefix(path, bpath) {
|
|
||||||
return name, os.ErrNotExist
|
|
||||||
}
|
|
||||||
|
|
||||||
return path, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateBasePathName(name string) error {
|
|
||||||
if runtime.GOOS != "windows" {
|
|
||||||
// Not much to do here;
|
|
||||||
// the virtual file paths all look absolute on *nix.
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// On Windows a common mistake would be to provide an absolute OS path
|
|
||||||
// We could strip out the base part, but that would not be very portable.
|
|
||||||
if filepath.IsAbs(name) {
|
|
||||||
return os.ErrNotExist
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *BasePathFs) Chtimes(name string, atime, mtime time.Time) (err error) {
|
|
||||||
if name, err = b.RealPath(name); err != nil {
|
|
||||||
return &os.PathError{Op: "chtimes", Path: name, Err: err}
|
|
||||||
}
|
|
||||||
return b.source.Chtimes(name, atime, mtime)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *BasePathFs) Chmod(name string, mode os.FileMode) (err error) {
|
|
||||||
if name, err = b.RealPath(name); err != nil {
|
|
||||||
return &os.PathError{Op: "chmod", Path: name, Err: err}
|
|
||||||
}
|
|
||||||
return b.source.Chmod(name, mode)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *BasePathFs) Name() string {
|
|
||||||
return "BasePathFs"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *BasePathFs) Stat(name string) (fi os.FileInfo, err error) {
|
|
||||||
if name, err = b.RealPath(name); err != nil {
|
|
||||||
return nil, &os.PathError{Op: "stat", Path: name, Err: err}
|
|
||||||
}
|
|
||||||
return b.source.Stat(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *BasePathFs) Rename(oldname, newname string) (err error) {
|
|
||||||
if oldname, err = b.RealPath(oldname); err != nil {
|
|
||||||
return &os.PathError{Op: "rename", Path: oldname, Err: err}
|
|
||||||
}
|
|
||||||
if newname, err = b.RealPath(newname); err != nil {
|
|
||||||
return &os.PathError{Op: "rename", Path: newname, Err: err}
|
|
||||||
}
|
|
||||||
return b.source.Rename(oldname, newname)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *BasePathFs) RemoveAll(name string) (err error) {
|
|
||||||
if name, err = b.RealPath(name); err != nil {
|
|
||||||
return &os.PathError{Op: "remove_all", Path: name, Err: err}
|
|
||||||
}
|
|
||||||
return b.source.RemoveAll(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *BasePathFs) Remove(name string) (err error) {
|
|
||||||
if name, err = b.RealPath(name); err != nil {
|
|
||||||
return &os.PathError{Op: "remove", Path: name, Err: err}
|
|
||||||
}
|
|
||||||
return b.source.Remove(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *BasePathFs) OpenFile(name string, flag int, mode os.FileMode) (f File, err error) {
|
|
||||||
if name, err = b.RealPath(name); err != nil {
|
|
||||||
return nil, &os.PathError{Op: "openfile", Path: name, Err: err}
|
|
||||||
}
|
|
||||||
sourcef, err := b.source.OpenFile(name, flag, mode)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &BasePathFile{sourcef, b.path}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *BasePathFs) Open(name string) (f File, err error) {
|
|
||||||
if name, err = b.RealPath(name); err != nil {
|
|
||||||
return nil, &os.PathError{Op: "open", Path: name, Err: err}
|
|
||||||
}
|
|
||||||
sourcef, err := b.source.Open(name)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &BasePathFile{File: sourcef, path: b.path}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *BasePathFs) Mkdir(name string, mode os.FileMode) (err error) {
|
|
||||||
if name, err = b.RealPath(name); err != nil {
|
|
||||||
return &os.PathError{Op: "mkdir", Path: name, Err: err}
|
|
||||||
}
|
|
||||||
return b.source.Mkdir(name, mode)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *BasePathFs) MkdirAll(name string, mode os.FileMode) (err error) {
|
|
||||||
if name, err = b.RealPath(name); err != nil {
|
|
||||||
return &os.PathError{Op: "mkdir", Path: name, Err: err}
|
|
||||||
}
|
|
||||||
return b.source.MkdirAll(name, mode)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *BasePathFs) Create(name string) (f File, err error) {
|
|
||||||
if name, err = b.RealPath(name); err != nil {
|
|
||||||
return nil, &os.PathError{Op: "create", Path: name, Err: err}
|
|
||||||
}
|
|
||||||
sourcef, err := b.source.Create(name)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &BasePathFile{File: sourcef, path: b.path}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *BasePathFs) LstatIfPossible(name string) (os.FileInfo, bool, error) {
|
|
||||||
name, err := b.RealPath(name)
|
|
||||||
if err != nil {
|
|
||||||
return nil, false, &os.PathError{Op: "lstat", Path: name, Err: err}
|
|
||||||
}
|
|
||||||
if lstater, ok := b.source.(Lstater); ok {
|
|
||||||
return lstater.LstatIfPossible(name)
|
|
||||||
}
|
|
||||||
fi, err := b.source.Stat(name)
|
|
||||||
return fi, false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// vim: ts=4 sw=4 noexpandtab nolist syn=go
|
|
290
vendor/github.com/spf13/afero/cacheOnReadFs.go
generated
vendored
290
vendor/github.com/spf13/afero/cacheOnReadFs.go
generated
vendored
@ -1,290 +0,0 @@
|
|||||||
package afero
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"syscall"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// If the cache duration is 0, cache time will be unlimited, i.e. once
|
|
||||||
// a file is in the layer, the base will never be read again for this file.
|
|
||||||
//
|
|
||||||
// For cache times greater than 0, the modification time of a file is
|
|
||||||
// checked. Note that a lot of file system implementations only allow a
|
|
||||||
// resolution of a second for timestamps... or as the godoc for os.Chtimes()
|
|
||||||
// states: "The underlying filesystem may truncate or round the values to a
|
|
||||||
// less precise time unit."
|
|
||||||
//
|
|
||||||
// This caching union will forward all write calls also to the base file
|
|
||||||
// system first. To prevent writing to the base Fs, wrap it in a read-only
|
|
||||||
// filter - Note: this will also make the overlay read-only, for writing files
|
|
||||||
// in the overlay, use the overlay Fs directly, not via the union Fs.
|
|
||||||
type CacheOnReadFs struct {
|
|
||||||
base Fs
|
|
||||||
layer Fs
|
|
||||||
cacheTime time.Duration
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewCacheOnReadFs(base Fs, layer Fs, cacheTime time.Duration) Fs {
|
|
||||||
return &CacheOnReadFs{base: base, layer: layer, cacheTime: cacheTime}
|
|
||||||
}
|
|
||||||
|
|
||||||
type cacheState int
|
|
||||||
|
|
||||||
const (
|
|
||||||
// not present in the overlay, unknown if it exists in the base:
|
|
||||||
cacheMiss cacheState = iota
|
|
||||||
// present in the overlay and in base, base file is newer:
|
|
||||||
cacheStale
|
|
||||||
// present in the overlay - with cache time == 0 it may exist in the base,
|
|
||||||
// with cacheTime > 0 it exists in the base and is same age or newer in the
|
|
||||||
// overlay
|
|
||||||
cacheHit
|
|
||||||
// happens if someone writes directly to the overlay without
|
|
||||||
// going through this union
|
|
||||||
cacheLocal
|
|
||||||
)
|
|
||||||
|
|
||||||
func (u *CacheOnReadFs) cacheStatus(name string) (state cacheState, fi os.FileInfo, err error) {
|
|
||||||
var lfi, bfi os.FileInfo
|
|
||||||
lfi, err = u.layer.Stat(name)
|
|
||||||
if err == nil {
|
|
||||||
if u.cacheTime == 0 {
|
|
||||||
return cacheHit, lfi, nil
|
|
||||||
}
|
|
||||||
if lfi.ModTime().Add(u.cacheTime).Before(time.Now()) {
|
|
||||||
bfi, err = u.base.Stat(name)
|
|
||||||
if err != nil {
|
|
||||||
return cacheLocal, lfi, nil
|
|
||||||
}
|
|
||||||
if bfi.ModTime().After(lfi.ModTime()) {
|
|
||||||
return cacheStale, bfi, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return cacheHit, lfi, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if err == syscall.ENOENT || os.IsNotExist(err) {
|
|
||||||
return cacheMiss, nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return cacheMiss, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *CacheOnReadFs) copyToLayer(name string) error {
|
|
||||||
return copyToLayer(u.base, u.layer, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *CacheOnReadFs) Chtimes(name string, atime, mtime time.Time) error {
|
|
||||||
st, _, err := u.cacheStatus(name)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
switch st {
|
|
||||||
case cacheLocal:
|
|
||||||
case cacheHit:
|
|
||||||
err = u.base.Chtimes(name, atime, mtime)
|
|
||||||
case cacheStale, cacheMiss:
|
|
||||||
if err := u.copyToLayer(name); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = u.base.Chtimes(name, atime, mtime)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return u.layer.Chtimes(name, atime, mtime)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *CacheOnReadFs) Chmod(name string, mode os.FileMode) error {
|
|
||||||
st, _, err := u.cacheStatus(name)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
switch st {
|
|
||||||
case cacheLocal:
|
|
||||||
case cacheHit:
|
|
||||||
err = u.base.Chmod(name, mode)
|
|
||||||
case cacheStale, cacheMiss:
|
|
||||||
if err := u.copyToLayer(name); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = u.base.Chmod(name, mode)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return u.layer.Chmod(name, mode)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *CacheOnReadFs) Stat(name string) (os.FileInfo, error) {
|
|
||||||
st, fi, err := u.cacheStatus(name)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
switch st {
|
|
||||||
case cacheMiss:
|
|
||||||
return u.base.Stat(name)
|
|
||||||
default: // cacheStale has base, cacheHit and cacheLocal the layer os.FileInfo
|
|
||||||
return fi, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *CacheOnReadFs) Rename(oldname, newname string) error {
|
|
||||||
st, _, err := u.cacheStatus(oldname)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
switch st {
|
|
||||||
case cacheLocal:
|
|
||||||
case cacheHit:
|
|
||||||
err = u.base.Rename(oldname, newname)
|
|
||||||
case cacheStale, cacheMiss:
|
|
||||||
if err := u.copyToLayer(oldname); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = u.base.Rename(oldname, newname)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return u.layer.Rename(oldname, newname)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *CacheOnReadFs) Remove(name string) error {
|
|
||||||
st, _, err := u.cacheStatus(name)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
switch st {
|
|
||||||
case cacheLocal:
|
|
||||||
case cacheHit, cacheStale, cacheMiss:
|
|
||||||
err = u.base.Remove(name)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return u.layer.Remove(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *CacheOnReadFs) RemoveAll(name string) error {
|
|
||||||
st, _, err := u.cacheStatus(name)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
switch st {
|
|
||||||
case cacheLocal:
|
|
||||||
case cacheHit, cacheStale, cacheMiss:
|
|
||||||
err = u.base.RemoveAll(name)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return u.layer.RemoveAll(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *CacheOnReadFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
|
|
||||||
st, _, err := u.cacheStatus(name)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
switch st {
|
|
||||||
case cacheLocal, cacheHit:
|
|
||||||
default:
|
|
||||||
if err := u.copyToLayer(name); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if flag&(os.O_WRONLY|syscall.O_RDWR|os.O_APPEND|os.O_CREATE|os.O_TRUNC) != 0 {
|
|
||||||
bfi, err := u.base.OpenFile(name, flag, perm)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
lfi, err := u.layer.OpenFile(name, flag, perm)
|
|
||||||
if err != nil {
|
|
||||||
bfi.Close() // oops, what if O_TRUNC was set and file opening in the layer failed...?
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &UnionFile{Base: bfi, Layer: lfi}, nil
|
|
||||||
}
|
|
||||||
return u.layer.OpenFile(name, flag, perm)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *CacheOnReadFs) Open(name string) (File, error) {
|
|
||||||
st, fi, err := u.cacheStatus(name)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
switch st {
|
|
||||||
case cacheLocal:
|
|
||||||
return u.layer.Open(name)
|
|
||||||
|
|
||||||
case cacheMiss:
|
|
||||||
bfi, err := u.base.Stat(name)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if bfi.IsDir() {
|
|
||||||
return u.base.Open(name)
|
|
||||||
}
|
|
||||||
if err := u.copyToLayer(name); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return u.layer.Open(name)
|
|
||||||
|
|
||||||
case cacheStale:
|
|
||||||
if !fi.IsDir() {
|
|
||||||
if err := u.copyToLayer(name); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return u.layer.Open(name)
|
|
||||||
}
|
|
||||||
case cacheHit:
|
|
||||||
if !fi.IsDir() {
|
|
||||||
return u.layer.Open(name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// the dirs from cacheHit, cacheStale fall down here:
|
|
||||||
bfile, _ := u.base.Open(name)
|
|
||||||
lfile, err := u.layer.Open(name)
|
|
||||||
if err != nil && bfile == nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &UnionFile{Base: bfile, Layer: lfile}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *CacheOnReadFs) Mkdir(name string, perm os.FileMode) error {
|
|
||||||
err := u.base.Mkdir(name, perm)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return u.layer.MkdirAll(name, perm) // yes, MkdirAll... we cannot assume it exists in the cache
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *CacheOnReadFs) Name() string {
|
|
||||||
return "CacheOnReadFs"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *CacheOnReadFs) MkdirAll(name string, perm os.FileMode) error {
|
|
||||||
err := u.base.MkdirAll(name, perm)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return u.layer.MkdirAll(name, perm)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *CacheOnReadFs) Create(name string) (File, error) {
|
|
||||||
bfh, err := u.base.Create(name)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
lfh, err := u.layer.Create(name)
|
|
||||||
if err != nil {
|
|
||||||
// oops, see comment about OS_TRUNC above, should we remove? then we have to
|
|
||||||
// remember if the file did not exist before
|
|
||||||
bfh.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &UnionFile{Base: bfh, Layer: lfh}, nil
|
|
||||||
}
|
|
22
vendor/github.com/spf13/afero/const_bsds.go
generated
vendored
22
vendor/github.com/spf13/afero/const_bsds.go
generated
vendored
@ -1,22 +0,0 @@
|
|||||||
// Copyright © 2016 Steve Francia <spf@spf13.com>.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
// +build darwin openbsd freebsd netbsd dragonfly
|
|
||||||
|
|
||||||
package afero
|
|
||||||
|
|
||||||
import (
|
|
||||||
"syscall"
|
|
||||||
)
|
|
||||||
|
|
||||||
const BADFD = syscall.EBADF
|
|
25
vendor/github.com/spf13/afero/const_win_unix.go
generated
vendored
25
vendor/github.com/spf13/afero/const_win_unix.go
generated
vendored
@ -1,25 +0,0 @@
|
|||||||
// Copyright © 2016 Steve Francia <spf@spf13.com>.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
// +build !darwin
|
|
||||||
// +build !openbsd
|
|
||||||
// +build !freebsd
|
|
||||||
// +build !dragonfly
|
|
||||||
// +build !netbsd
|
|
||||||
|
|
||||||
package afero
|
|
||||||
|
|
||||||
import (
|
|
||||||
"syscall"
|
|
||||||
)
|
|
||||||
|
|
||||||
const BADFD = syscall.EBADFD
|
|
293
vendor/github.com/spf13/afero/copyOnWriteFs.go
generated
vendored
293
vendor/github.com/spf13/afero/copyOnWriteFs.go
generated
vendored
@ -1,293 +0,0 @@
|
|||||||
package afero
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"syscall"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var _ Lstater = (*CopyOnWriteFs)(nil)
|
|
||||||
|
|
||||||
// The CopyOnWriteFs is a union filesystem: a read only base file system with
|
|
||||||
// a possibly writeable layer on top. Changes to the file system will only
|
|
||||||
// be made in the overlay: Changing an existing file in the base layer which
|
|
||||||
// is not present in the overlay will copy the file to the overlay ("changing"
|
|
||||||
// includes also calls to e.g. Chtimes() and Chmod()).
|
|
||||||
//
|
|
||||||
// Reading directories is currently only supported via Open(), not OpenFile().
|
|
||||||
type CopyOnWriteFs struct {
|
|
||||||
base Fs
|
|
||||||
layer Fs
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewCopyOnWriteFs(base Fs, layer Fs) Fs {
|
|
||||||
return &CopyOnWriteFs{base: base, layer: layer}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns true if the file is not in the overlay
|
|
||||||
func (u *CopyOnWriteFs) isBaseFile(name string) (bool, error) {
|
|
||||||
if _, err := u.layer.Stat(name); err == nil {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
_, err := u.base.Stat(name)
|
|
||||||
if err != nil {
|
|
||||||
if oerr, ok := err.(*os.PathError); ok {
|
|
||||||
if oerr.Err == os.ErrNotExist || oerr.Err == syscall.ENOENT || oerr.Err == syscall.ENOTDIR {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err == syscall.ENOENT {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *CopyOnWriteFs) copyToLayer(name string) error {
|
|
||||||
return copyToLayer(u.base, u.layer, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *CopyOnWriteFs) Chtimes(name string, atime, mtime time.Time) error {
|
|
||||||
b, err := u.isBaseFile(name)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if b {
|
|
||||||
if err := u.copyToLayer(name); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return u.layer.Chtimes(name, atime, mtime)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *CopyOnWriteFs) Chmod(name string, mode os.FileMode) error {
|
|
||||||
b, err := u.isBaseFile(name)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if b {
|
|
||||||
if err := u.copyToLayer(name); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return u.layer.Chmod(name, mode)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *CopyOnWriteFs) Stat(name string) (os.FileInfo, error) {
|
|
||||||
fi, err := u.layer.Stat(name)
|
|
||||||
if err != nil {
|
|
||||||
isNotExist := u.isNotExist(err)
|
|
||||||
if isNotExist {
|
|
||||||
return u.base.Stat(name)
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return fi, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *CopyOnWriteFs) LstatIfPossible(name string) (os.FileInfo, bool, error) {
|
|
||||||
llayer, ok1 := u.layer.(Lstater)
|
|
||||||
lbase, ok2 := u.base.(Lstater)
|
|
||||||
|
|
||||||
if ok1 {
|
|
||||||
fi, b, err := llayer.LstatIfPossible(name)
|
|
||||||
if err == nil {
|
|
||||||
return fi, b, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if !u.isNotExist(err) {
|
|
||||||
return nil, b, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ok2 {
|
|
||||||
fi, b, err := lbase.LstatIfPossible(name)
|
|
||||||
if err == nil {
|
|
||||||
return fi, b, nil
|
|
||||||
}
|
|
||||||
if !u.isNotExist(err) {
|
|
||||||
return nil, b, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fi, err := u.Stat(name)
|
|
||||||
|
|
||||||
return fi, false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *CopyOnWriteFs) isNotExist(err error) bool {
|
|
||||||
if e, ok := err.(*os.PathError); ok {
|
|
||||||
err = e.Err
|
|
||||||
}
|
|
||||||
if err == os.ErrNotExist || err == syscall.ENOENT || err == syscall.ENOTDIR {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Renaming files present only in the base layer is not permitted
|
|
||||||
func (u *CopyOnWriteFs) Rename(oldname, newname string) error {
|
|
||||||
b, err := u.isBaseFile(oldname)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if b {
|
|
||||||
return syscall.EPERM
|
|
||||||
}
|
|
||||||
return u.layer.Rename(oldname, newname)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Removing files present only in the base layer is not permitted. If
|
|
||||||
// a file is present in the base layer and the overlay, only the overlay
|
|
||||||
// will be removed.
|
|
||||||
func (u *CopyOnWriteFs) Remove(name string) error {
|
|
||||||
err := u.layer.Remove(name)
|
|
||||||
switch err {
|
|
||||||
case syscall.ENOENT:
|
|
||||||
_, err = u.base.Stat(name)
|
|
||||||
if err == nil {
|
|
||||||
return syscall.EPERM
|
|
||||||
}
|
|
||||||
return syscall.ENOENT
|
|
||||||
default:
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *CopyOnWriteFs) RemoveAll(name string) error {
|
|
||||||
err := u.layer.RemoveAll(name)
|
|
||||||
switch err {
|
|
||||||
case syscall.ENOENT:
|
|
||||||
_, err = u.base.Stat(name)
|
|
||||||
if err == nil {
|
|
||||||
return syscall.EPERM
|
|
||||||
}
|
|
||||||
return syscall.ENOENT
|
|
||||||
default:
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *CopyOnWriteFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
|
|
||||||
b, err := u.isBaseFile(name)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if flag&(os.O_WRONLY|os.O_RDWR|os.O_APPEND|os.O_CREATE|os.O_TRUNC) != 0 {
|
|
||||||
if b {
|
|
||||||
if err = u.copyToLayer(name); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return u.layer.OpenFile(name, flag, perm)
|
|
||||||
}
|
|
||||||
|
|
||||||
dir := filepath.Dir(name)
|
|
||||||
isaDir, err := IsDir(u.base, dir)
|
|
||||||
if err != nil && !os.IsNotExist(err) {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if isaDir {
|
|
||||||
if err = u.layer.MkdirAll(dir, 0777); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return u.layer.OpenFile(name, flag, perm)
|
|
||||||
}
|
|
||||||
|
|
||||||
isaDir, err = IsDir(u.layer, dir)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if isaDir {
|
|
||||||
return u.layer.OpenFile(name, flag, perm)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, &os.PathError{Op: "open", Path: name, Err: syscall.ENOTDIR} // ...or os.ErrNotExist?
|
|
||||||
}
|
|
||||||
if b {
|
|
||||||
return u.base.OpenFile(name, flag, perm)
|
|
||||||
}
|
|
||||||
return u.layer.OpenFile(name, flag, perm)
|
|
||||||
}
|
|
||||||
|
|
||||||
// This function handles the 9 different possibilities caused
|
|
||||||
// by the union which are the intersection of the following...
|
|
||||||
// layer: doesn't exist, exists as a file, and exists as a directory
|
|
||||||
// base: doesn't exist, exists as a file, and exists as a directory
|
|
||||||
func (u *CopyOnWriteFs) Open(name string) (File, error) {
|
|
||||||
// Since the overlay overrides the base we check that first
|
|
||||||
b, err := u.isBaseFile(name)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// If overlay doesn't exist, return the base (base state irrelevant)
|
|
||||||
if b {
|
|
||||||
return u.base.Open(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// If overlay is a file, return it (base state irrelevant)
|
|
||||||
dir, err := IsDir(u.layer, name)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if !dir {
|
|
||||||
return u.layer.Open(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Overlay is a directory, base state now matters.
|
|
||||||
// Base state has 3 states to check but 2 outcomes:
|
|
||||||
// A. It's a file or non-readable in the base (return just the overlay)
|
|
||||||
// B. It's an accessible directory in the base (return a UnionFile)
|
|
||||||
|
|
||||||
// If base is file or nonreadable, return overlay
|
|
||||||
dir, err = IsDir(u.base, name)
|
|
||||||
if !dir || err != nil {
|
|
||||||
return u.layer.Open(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Both base & layer are directories
|
|
||||||
// Return union file (if opens are without error)
|
|
||||||
bfile, bErr := u.base.Open(name)
|
|
||||||
lfile, lErr := u.layer.Open(name)
|
|
||||||
|
|
||||||
// If either have errors at this point something is very wrong. Return nil and the errors
|
|
||||||
if bErr != nil || lErr != nil {
|
|
||||||
return nil, fmt.Errorf("BaseErr: %v\nOverlayErr: %v", bErr, lErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &UnionFile{Base: bfile, Layer: lfile}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *CopyOnWriteFs) Mkdir(name string, perm os.FileMode) error {
|
|
||||||
dir, err := IsDir(u.base, name)
|
|
||||||
if err != nil {
|
|
||||||
return u.layer.MkdirAll(name, perm)
|
|
||||||
}
|
|
||||||
if dir {
|
|
||||||
return ErrFileExists
|
|
||||||
}
|
|
||||||
return u.layer.MkdirAll(name, perm)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *CopyOnWriteFs) Name() string {
|
|
||||||
return "CopyOnWriteFs"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *CopyOnWriteFs) MkdirAll(name string, perm os.FileMode) error {
|
|
||||||
dir, err := IsDir(u.base, name)
|
|
||||||
if err != nil {
|
|
||||||
return u.layer.MkdirAll(name, perm)
|
|
||||||
}
|
|
||||||
if dir {
|
|
||||||
// This is in line with how os.MkdirAll behaves.
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return u.layer.MkdirAll(name, perm)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *CopyOnWriteFs) Create(name string) (File, error) {
|
|
||||||
return u.OpenFile(name, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0666)
|
|
||||||
}
|
|
3
vendor/github.com/spf13/afero/go.mod
generated
vendored
3
vendor/github.com/spf13/afero/go.mod
generated
vendored
@ -1,3 +0,0 @@
|
|||||||
module github.com/spf13/afero
|
|
||||||
|
|
||||||
require golang.org/x/text v0.3.0
|
|
2
vendor/github.com/spf13/afero/go.sum
generated
vendored
2
vendor/github.com/spf13/afero/go.sum
generated
vendored
@ -1,2 +0,0 @@
|
|||||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
|
110
vendor/github.com/spf13/afero/httpFs.go
generated
vendored
110
vendor/github.com/spf13/afero/httpFs.go
generated
vendored
@ -1,110 +0,0 @@
|
|||||||
// Copyright © 2014 Steve Francia <spf@spf13.com>.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package afero
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type httpDir struct {
|
|
||||||
basePath string
|
|
||||||
fs HttpFs
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d httpDir) Open(name string) (http.File, error) {
|
|
||||||
if filepath.Separator != '/' && strings.IndexRune(name, filepath.Separator) >= 0 ||
|
|
||||||
strings.Contains(name, "\x00") {
|
|
||||||
return nil, errors.New("http: invalid character in file path")
|
|
||||||
}
|
|
||||||
dir := string(d.basePath)
|
|
||||||
if dir == "" {
|
|
||||||
dir = "."
|
|
||||||
}
|
|
||||||
|
|
||||||
f, err := d.fs.Open(filepath.Join(dir, filepath.FromSlash(path.Clean("/"+name))))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return f, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type HttpFs struct {
|
|
||||||
source Fs
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewHttpFs(source Fs) *HttpFs {
|
|
||||||
return &HttpFs{source: source}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h HttpFs) Dir(s string) *httpDir {
|
|
||||||
return &httpDir{basePath: s, fs: h}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h HttpFs) Name() string { return "h HttpFs" }
|
|
||||||
|
|
||||||
func (h HttpFs) Create(name string) (File, error) {
|
|
||||||
return h.source.Create(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h HttpFs) Chmod(name string, mode os.FileMode) error {
|
|
||||||
return h.source.Chmod(name, mode)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h HttpFs) Chtimes(name string, atime time.Time, mtime time.Time) error {
|
|
||||||
return h.source.Chtimes(name, atime, mtime)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h HttpFs) Mkdir(name string, perm os.FileMode) error {
|
|
||||||
return h.source.Mkdir(name, perm)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h HttpFs) MkdirAll(path string, perm os.FileMode) error {
|
|
||||||
return h.source.MkdirAll(path, perm)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h HttpFs) Open(name string) (http.File, error) {
|
|
||||||
f, err := h.source.Open(name)
|
|
||||||
if err == nil {
|
|
||||||
if httpfile, ok := f.(http.File); ok {
|
|
||||||
return httpfile, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h HttpFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
|
|
||||||
return h.source.OpenFile(name, flag, perm)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h HttpFs) Remove(name string) error {
|
|
||||||
return h.source.Remove(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h HttpFs) RemoveAll(path string) error {
|
|
||||||
return h.source.RemoveAll(path)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h HttpFs) Rename(oldname, newname string) error {
|
|
||||||
return h.source.Rename(oldname, newname)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h HttpFs) Stat(name string) (os.FileInfo, error) {
|
|
||||||
return h.source.Stat(name)
|
|
||||||
}
|
|
230
vendor/github.com/spf13/afero/ioutil.go
generated
vendored
230
vendor/github.com/spf13/afero/ioutil.go
generated
vendored
@ -1,230 +0,0 @@
|
|||||||
// Copyright ©2015 The Go Authors
|
|
||||||
// Copyright ©2015 Steve Francia <spf@spf13.com>
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package afero
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"sort"
|
|
||||||
"strconv"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// byName implements sort.Interface.
|
|
||||||
type byName []os.FileInfo
|
|
||||||
|
|
||||||
func (f byName) Len() int { return len(f) }
|
|
||||||
func (f byName) Less(i, j int) bool { return f[i].Name() < f[j].Name() }
|
|
||||||
func (f byName) Swap(i, j int) { f[i], f[j] = f[j], f[i] }
|
|
||||||
|
|
||||||
// ReadDir reads the directory named by dirname and returns
|
|
||||||
// a list of sorted directory entries.
|
|
||||||
func (a Afero) ReadDir(dirname string) ([]os.FileInfo, error) {
|
|
||||||
return ReadDir(a.Fs, dirname)
|
|
||||||
}
|
|
||||||
|
|
||||||
func ReadDir(fs Fs, dirname string) ([]os.FileInfo, error) {
|
|
||||||
f, err := fs.Open(dirname)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
list, err := f.Readdir(-1)
|
|
||||||
f.Close()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
sort.Sort(byName(list))
|
|
||||||
return list, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadFile reads the file named by filename and returns the contents.
|
|
||||||
// A successful call returns err == nil, not err == EOF. Because ReadFile
|
|
||||||
// reads the whole file, it does not treat an EOF from Read as an error
|
|
||||||
// to be reported.
|
|
||||||
func (a Afero) ReadFile(filename string) ([]byte, error) {
|
|
||||||
return ReadFile(a.Fs, filename)
|
|
||||||
}
|
|
||||||
|
|
||||||
func ReadFile(fs Fs, filename string) ([]byte, error) {
|
|
||||||
f, err := fs.Open(filename)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
// It's a good but not certain bet that FileInfo will tell us exactly how much to
|
|
||||||
// read, so let's try it but be prepared for the answer to be wrong.
|
|
||||||
var n int64
|
|
||||||
|
|
||||||
if fi, err := f.Stat(); err == nil {
|
|
||||||
// Don't preallocate a huge buffer, just in case.
|
|
||||||
if size := fi.Size(); size < 1e9 {
|
|
||||||
n = size
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// As initial capacity for readAll, use n + a little extra in case Size is zero,
|
|
||||||
// and to avoid another allocation after Read has filled the buffer. The readAll
|
|
||||||
// call will read into its allocated internal buffer cheaply. If the size was
|
|
||||||
// wrong, we'll either waste some space off the end or reallocate as needed, but
|
|
||||||
// in the overwhelmingly common case we'll get it just right.
|
|
||||||
return readAll(f, n+bytes.MinRead)
|
|
||||||
}
|
|
||||||
|
|
||||||
// readAll reads from r until an error or EOF and returns the data it read
|
|
||||||
// from the internal buffer allocated with a specified capacity.
|
|
||||||
func readAll(r io.Reader, capacity int64) (b []byte, err error) {
|
|
||||||
buf := bytes.NewBuffer(make([]byte, 0, capacity))
|
|
||||||
// If the buffer overflows, we will get bytes.ErrTooLarge.
|
|
||||||
// Return that as an error. Any other panic remains.
|
|
||||||
defer func() {
|
|
||||||
e := recover()
|
|
||||||
if e == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if panicErr, ok := e.(error); ok && panicErr == bytes.ErrTooLarge {
|
|
||||||
err = panicErr
|
|
||||||
} else {
|
|
||||||
panic(e)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
_, err = buf.ReadFrom(r)
|
|
||||||
return buf.Bytes(), err
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadAll reads from r until an error or EOF and returns the data it read.
|
|
||||||
// A successful call returns err == nil, not err == EOF. Because ReadAll is
|
|
||||||
// defined to read from src until EOF, it does not treat an EOF from Read
|
|
||||||
// as an error to be reported.
|
|
||||||
func ReadAll(r io.Reader) ([]byte, error) {
|
|
||||||
return readAll(r, bytes.MinRead)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteFile writes data to a file named by filename.
|
|
||||||
// If the file does not exist, WriteFile creates it with permissions perm;
|
|
||||||
// otherwise WriteFile truncates it before writing.
|
|
||||||
func (a Afero) WriteFile(filename string, data []byte, perm os.FileMode) error {
|
|
||||||
return WriteFile(a.Fs, filename, data, perm)
|
|
||||||
}
|
|
||||||
|
|
||||||
func WriteFile(fs Fs, filename string, data []byte, perm os.FileMode) error {
|
|
||||||
f, err := fs.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
n, err := f.Write(data)
|
|
||||||
if err == nil && n < len(data) {
|
|
||||||
err = io.ErrShortWrite
|
|
||||||
}
|
|
||||||
if err1 := f.Close(); err == nil {
|
|
||||||
err = err1
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Random number state.
|
|
||||||
// We generate random temporary file names so that there's a good
|
|
||||||
// chance the file doesn't exist yet - keeps the number of tries in
|
|
||||||
// TempFile to a minimum.
|
|
||||||
var rand uint32
|
|
||||||
var randmu sync.Mutex
|
|
||||||
|
|
||||||
func reseed() uint32 {
|
|
||||||
return uint32(time.Now().UnixNano() + int64(os.Getpid()))
|
|
||||||
}
|
|
||||||
|
|
||||||
func nextSuffix() string {
|
|
||||||
randmu.Lock()
|
|
||||||
r := rand
|
|
||||||
if r == 0 {
|
|
||||||
r = reseed()
|
|
||||||
}
|
|
||||||
r = r*1664525 + 1013904223 // constants from Numerical Recipes
|
|
||||||
rand = r
|
|
||||||
randmu.Unlock()
|
|
||||||
return strconv.Itoa(int(1e9 + r%1e9))[1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
// TempFile creates a new temporary file in the directory dir
|
|
||||||
// with a name beginning with prefix, opens the file for reading
|
|
||||||
// and writing, and returns the resulting *File.
|
|
||||||
// If dir is the empty string, TempFile uses the default directory
|
|
||||||
// for temporary files (see os.TempDir).
|
|
||||||
// Multiple programs calling TempFile simultaneously
|
|
||||||
// will not choose the same file. The caller can use f.Name()
|
|
||||||
// to find the pathname of the file. It is the caller's responsibility
|
|
||||||
// to remove the file when no longer needed.
|
|
||||||
func (a Afero) TempFile(dir, prefix string) (f File, err error) {
|
|
||||||
return TempFile(a.Fs, dir, prefix)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TempFile(fs Fs, dir, prefix string) (f File, err error) {
|
|
||||||
if dir == "" {
|
|
||||||
dir = os.TempDir()
|
|
||||||
}
|
|
||||||
|
|
||||||
nconflict := 0
|
|
||||||
for i := 0; i < 10000; i++ {
|
|
||||||
name := filepath.Join(dir, prefix+nextSuffix())
|
|
||||||
f, err = fs.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600)
|
|
||||||
if os.IsExist(err) {
|
|
||||||
if nconflict++; nconflict > 10 {
|
|
||||||
randmu.Lock()
|
|
||||||
rand = reseed()
|
|
||||||
randmu.Unlock()
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// TempDir creates a new temporary directory in the directory dir
|
|
||||||
// with a name beginning with prefix and returns the path of the
|
|
||||||
// new directory. If dir is the empty string, TempDir uses the
|
|
||||||
// default directory for temporary files (see os.TempDir).
|
|
||||||
// Multiple programs calling TempDir simultaneously
|
|
||||||
// will not choose the same directory. It is the caller's responsibility
|
|
||||||
// to remove the directory when no longer needed.
|
|
||||||
func (a Afero) TempDir(dir, prefix string) (name string, err error) {
|
|
||||||
return TempDir(a.Fs, dir, prefix)
|
|
||||||
}
|
|
||||||
func TempDir(fs Fs, dir, prefix string) (name string, err error) {
|
|
||||||
if dir == "" {
|
|
||||||
dir = os.TempDir()
|
|
||||||
}
|
|
||||||
|
|
||||||
nconflict := 0
|
|
||||||
for i := 0; i < 10000; i++ {
|
|
||||||
try := filepath.Join(dir, prefix+nextSuffix())
|
|
||||||
err = fs.Mkdir(try, 0700)
|
|
||||||
if os.IsExist(err) {
|
|
||||||
if nconflict++; nconflict > 10 {
|
|
||||||
randmu.Lock()
|
|
||||||
rand = reseed()
|
|
||||||
randmu.Unlock()
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
name = try
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
27
vendor/github.com/spf13/afero/lstater.go
generated
vendored
27
vendor/github.com/spf13/afero/lstater.go
generated
vendored
@ -1,27 +0,0 @@
|
|||||||
// Copyright © 2018 Steve Francia <spf@spf13.com>.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package afero
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Lstater is an optional interface in Afero. It is only implemented by the
|
|
||||||
// filesystems saying so.
|
|
||||||
// It will call Lstat if the filesystem iself is, or it delegates to, the os filesystem.
|
|
||||||
// Else it will call Stat.
|
|
||||||
// In addtion to the FileInfo, it will return a boolean telling whether Lstat was called or not.
|
|
||||||
type Lstater interface {
|
|
||||||
LstatIfPossible(name string) (os.FileInfo, bool, error)
|
|
||||||
}
|
|
110
vendor/github.com/spf13/afero/match.go
generated
vendored
110
vendor/github.com/spf13/afero/match.go
generated
vendored
@ -1,110 +0,0 @@
|
|||||||
// Copyright © 2014 Steve Francia <spf@spf13.com>.
|
|
||||||
// Copyright 2009 The Go Authors. All rights reserved.
|
|
||||||
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package afero
|
|
||||||
|
|
||||||
import (
|
|
||||||
"path/filepath"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Glob returns the names of all files matching pattern or nil
|
|
||||||
// if there is no matching file. The syntax of patterns is the same
|
|
||||||
// as in Match. The pattern may describe hierarchical names such as
|
|
||||||
// /usr/*/bin/ed (assuming the Separator is '/').
|
|
||||||
//
|
|
||||||
// Glob ignores file system errors such as I/O errors reading directories.
|
|
||||||
// The only possible returned error is ErrBadPattern, when pattern
|
|
||||||
// is malformed.
|
|
||||||
//
|
|
||||||
// This was adapted from (http://golang.org/pkg/path/filepath) and uses several
|
|
||||||
// built-ins from that package.
|
|
||||||
func Glob(fs Fs, pattern string) (matches []string, err error) {
|
|
||||||
if !hasMeta(pattern) {
|
|
||||||
// Lstat not supported by a ll filesystems.
|
|
||||||
if _, err = lstatIfPossible(fs, pattern); err != nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
return []string{pattern}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
dir, file := filepath.Split(pattern)
|
|
||||||
switch dir {
|
|
||||||
case "":
|
|
||||||
dir = "."
|
|
||||||
case string(filepath.Separator):
|
|
||||||
// nothing
|
|
||||||
default:
|
|
||||||
dir = dir[0 : len(dir)-1] // chop off trailing separator
|
|
||||||
}
|
|
||||||
|
|
||||||
if !hasMeta(dir) {
|
|
||||||
return glob(fs, dir, file, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
var m []string
|
|
||||||
m, err = Glob(fs, dir)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, d := range m {
|
|
||||||
matches, err = glob(fs, d, file, matches)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// glob searches for files matching pattern in the directory dir
|
|
||||||
// and appends them to matches. If the directory cannot be
|
|
||||||
// opened, it returns the existing matches. New matches are
|
|
||||||
// added in lexicographical order.
|
|
||||||
func glob(fs Fs, dir, pattern string, matches []string) (m []string, e error) {
|
|
||||||
m = matches
|
|
||||||
fi, err := fs.Stat(dir)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !fi.IsDir() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
d, err := fs.Open(dir)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer d.Close()
|
|
||||||
|
|
||||||
names, _ := d.Readdirnames(-1)
|
|
||||||
sort.Strings(names)
|
|
||||||
|
|
||||||
for _, n := range names {
|
|
||||||
matched, err := filepath.Match(pattern, n)
|
|
||||||
if err != nil {
|
|
||||||
return m, err
|
|
||||||
}
|
|
||||||
if matched {
|
|
||||||
m = append(m, filepath.Join(dir, n))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// hasMeta reports whether path contains any of the magic characters
|
|
||||||
// recognized by Match.
|
|
||||||
func hasMeta(path string) bool {
|
|
||||||
// TODO(niemeyer): Should other magic characters be added here?
|
|
||||||
return strings.IndexAny(path, "*?[") >= 0
|
|
||||||
}
|
|
37
vendor/github.com/spf13/afero/mem/dir.go
generated
vendored
37
vendor/github.com/spf13/afero/mem/dir.go
generated
vendored
@ -1,37 +0,0 @@
|
|||||||
// Copyright © 2014 Steve Francia <spf@spf13.com>.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package mem
|
|
||||||
|
|
||||||
type Dir interface {
|
|
||||||
Len() int
|
|
||||||
Names() []string
|
|
||||||
Files() []*FileData
|
|
||||||
Add(*FileData)
|
|
||||||
Remove(*FileData)
|
|
||||||
}
|
|
||||||
|
|
||||||
func RemoveFromMemDir(dir *FileData, f *FileData) {
|
|
||||||
dir.memDir.Remove(f)
|
|
||||||
}
|
|
||||||
|
|
||||||
func AddToMemDir(dir *FileData, f *FileData) {
|
|
||||||
dir.memDir.Add(f)
|
|
||||||
}
|
|
||||||
|
|
||||||
func InitializeDir(d *FileData) {
|
|
||||||
if d.memDir == nil {
|
|
||||||
d.dir = true
|
|
||||||
d.memDir = &DirMap{}
|
|
||||||
}
|
|
||||||
}
|
|
43
vendor/github.com/spf13/afero/mem/dirmap.go
generated
vendored
43
vendor/github.com/spf13/afero/mem/dirmap.go
generated
vendored
@ -1,43 +0,0 @@
|
|||||||
// Copyright © 2015 Steve Francia <spf@spf13.com>.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package mem
|
|
||||||
|
|
||||||
import "sort"
|
|
||||||
|
|
||||||
type DirMap map[string]*FileData
|
|
||||||
|
|
||||||
func (m DirMap) Len() int { return len(m) }
|
|
||||||
func (m DirMap) Add(f *FileData) { m[f.name] = f }
|
|
||||||
func (m DirMap) Remove(f *FileData) { delete(m, f.name) }
|
|
||||||
func (m DirMap) Files() (files []*FileData) {
|
|
||||||
for _, f := range m {
|
|
||||||
files = append(files, f)
|
|
||||||
}
|
|
||||||
sort.Sort(filesSorter(files))
|
|
||||||
return files
|
|
||||||
}
|
|
||||||
|
|
||||||
// implement sort.Interface for []*FileData
|
|
||||||
type filesSorter []*FileData
|
|
||||||
|
|
||||||
func (s filesSorter) Len() int { return len(s) }
|
|
||||||
func (s filesSorter) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
|
||||||
func (s filesSorter) Less(i, j int) bool { return s[i].name < s[j].name }
|
|
||||||
|
|
||||||
func (m DirMap) Names() (names []string) {
|
|
||||||
for x := range m {
|
|
||||||
names = append(names, x)
|
|
||||||
}
|
|
||||||
return names
|
|
||||||
}
|
|
317
vendor/github.com/spf13/afero/mem/file.go
generated
vendored
317
vendor/github.com/spf13/afero/mem/file.go
generated
vendored
@ -1,317 +0,0 @@
|
|||||||
// Copyright © 2015 Steve Francia <spf@spf13.com>.
|
|
||||||
// Copyright 2013 tsuru authors. All rights reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package mem
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"errors"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"sync"
|
|
||||||
"sync/atomic"
|
|
||||||
)
|
|
||||||
|
|
||||||
import "time"
|
|
||||||
|
|
||||||
const FilePathSeparator = string(filepath.Separator)
|
|
||||||
|
|
||||||
type File struct {
|
|
||||||
// atomic requires 64-bit alignment for struct field access
|
|
||||||
at int64
|
|
||||||
readDirCount int64
|
|
||||||
closed bool
|
|
||||||
readOnly bool
|
|
||||||
fileData *FileData
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewFileHandle(data *FileData) *File {
|
|
||||||
return &File{fileData: data}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewReadOnlyFileHandle(data *FileData) *File {
|
|
||||||
return &File{fileData: data, readOnly: true}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f File) Data() *FileData {
|
|
||||||
return f.fileData
|
|
||||||
}
|
|
||||||
|
|
||||||
type FileData struct {
|
|
||||||
sync.Mutex
|
|
||||||
name string
|
|
||||||
data []byte
|
|
||||||
memDir Dir
|
|
||||||
dir bool
|
|
||||||
mode os.FileMode
|
|
||||||
modtime time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *FileData) Name() string {
|
|
||||||
d.Lock()
|
|
||||||
defer d.Unlock()
|
|
||||||
return d.name
|
|
||||||
}
|
|
||||||
|
|
||||||
func CreateFile(name string) *FileData {
|
|
||||||
return &FileData{name: name, mode: os.ModeTemporary, modtime: time.Now()}
|
|
||||||
}
|
|
||||||
|
|
||||||
func CreateDir(name string) *FileData {
|
|
||||||
return &FileData{name: name, memDir: &DirMap{}, dir: true}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ChangeFileName(f *FileData, newname string) {
|
|
||||||
f.Lock()
|
|
||||||
f.name = newname
|
|
||||||
f.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func SetMode(f *FileData, mode os.FileMode) {
|
|
||||||
f.Lock()
|
|
||||||
f.mode = mode
|
|
||||||
f.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func SetModTime(f *FileData, mtime time.Time) {
|
|
||||||
f.Lock()
|
|
||||||
setModTime(f, mtime)
|
|
||||||
f.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func setModTime(f *FileData, mtime time.Time) {
|
|
||||||
f.modtime = mtime
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetFileInfo(f *FileData) *FileInfo {
|
|
||||||
return &FileInfo{f}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *File) Open() error {
|
|
||||||
atomic.StoreInt64(&f.at, 0)
|
|
||||||
atomic.StoreInt64(&f.readDirCount, 0)
|
|
||||||
f.fileData.Lock()
|
|
||||||
f.closed = false
|
|
||||||
f.fileData.Unlock()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *File) Close() error {
|
|
||||||
f.fileData.Lock()
|
|
||||||
f.closed = true
|
|
||||||
if !f.readOnly {
|
|
||||||
setModTime(f.fileData, time.Now())
|
|
||||||
}
|
|
||||||
f.fileData.Unlock()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *File) Name() string {
|
|
||||||
return f.fileData.Name()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *File) Stat() (os.FileInfo, error) {
|
|
||||||
return &FileInfo{f.fileData}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *File) Sync() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *File) Readdir(count int) (res []os.FileInfo, err error) {
|
|
||||||
if !f.fileData.dir {
|
|
||||||
return nil, &os.PathError{Op: "readdir", Path: f.fileData.name, Err: errors.New("not a dir")}
|
|
||||||
}
|
|
||||||
var outLength int64
|
|
||||||
|
|
||||||
f.fileData.Lock()
|
|
||||||
files := f.fileData.memDir.Files()[f.readDirCount:]
|
|
||||||
if count > 0 {
|
|
||||||
if len(files) < count {
|
|
||||||
outLength = int64(len(files))
|
|
||||||
} else {
|
|
||||||
outLength = int64(count)
|
|
||||||
}
|
|
||||||
if len(files) == 0 {
|
|
||||||
err = io.EOF
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
outLength = int64(len(files))
|
|
||||||
}
|
|
||||||
f.readDirCount += outLength
|
|
||||||
f.fileData.Unlock()
|
|
||||||
|
|
||||||
res = make([]os.FileInfo, outLength)
|
|
||||||
for i := range res {
|
|
||||||
res[i] = &FileInfo{files[i]}
|
|
||||||
}
|
|
||||||
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *File) Readdirnames(n int) (names []string, err error) {
|
|
||||||
fi, err := f.Readdir(n)
|
|
||||||
names = make([]string, len(fi))
|
|
||||||
for i, f := range fi {
|
|
||||||
_, names[i] = filepath.Split(f.Name())
|
|
||||||
}
|
|
||||||
return names, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *File) Read(b []byte) (n int, err error) {
|
|
||||||
f.fileData.Lock()
|
|
||||||
defer f.fileData.Unlock()
|
|
||||||
if f.closed == true {
|
|
||||||
return 0, ErrFileClosed
|
|
||||||
}
|
|
||||||
if len(b) > 0 && int(f.at) == len(f.fileData.data) {
|
|
||||||
return 0, io.EOF
|
|
||||||
}
|
|
||||||
if int(f.at) > len(f.fileData.data) {
|
|
||||||
return 0, io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
if len(f.fileData.data)-int(f.at) >= len(b) {
|
|
||||||
n = len(b)
|
|
||||||
} else {
|
|
||||||
n = len(f.fileData.data) - int(f.at)
|
|
||||||
}
|
|
||||||
copy(b, f.fileData.data[f.at:f.at+int64(n)])
|
|
||||||
atomic.AddInt64(&f.at, int64(n))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *File) ReadAt(b []byte, off int64) (n int, err error) {
|
|
||||||
atomic.StoreInt64(&f.at, off)
|
|
||||||
return f.Read(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *File) Truncate(size int64) error {
|
|
||||||
if f.closed == true {
|
|
||||||
return ErrFileClosed
|
|
||||||
}
|
|
||||||
if f.readOnly {
|
|
||||||
return &os.PathError{Op: "truncate", Path: f.fileData.name, Err: errors.New("file handle is read only")}
|
|
||||||
}
|
|
||||||
if size < 0 {
|
|
||||||
return ErrOutOfRange
|
|
||||||
}
|
|
||||||
if size > int64(len(f.fileData.data)) {
|
|
||||||
diff := size - int64(len(f.fileData.data))
|
|
||||||
f.fileData.data = append(f.fileData.data, bytes.Repeat([]byte{00}, int(diff))...)
|
|
||||||
} else {
|
|
||||||
f.fileData.data = f.fileData.data[0:size]
|
|
||||||
}
|
|
||||||
setModTime(f.fileData, time.Now())
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *File) Seek(offset int64, whence int) (int64, error) {
|
|
||||||
if f.closed == true {
|
|
||||||
return 0, ErrFileClosed
|
|
||||||
}
|
|
||||||
switch whence {
|
|
||||||
case 0:
|
|
||||||
atomic.StoreInt64(&f.at, offset)
|
|
||||||
case 1:
|
|
||||||
atomic.AddInt64(&f.at, int64(offset))
|
|
||||||
case 2:
|
|
||||||
atomic.StoreInt64(&f.at, int64(len(f.fileData.data))+offset)
|
|
||||||
}
|
|
||||||
return f.at, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *File) Write(b []byte) (n int, err error) {
|
|
||||||
if f.readOnly {
|
|
||||||
return 0, &os.PathError{Op: "write", Path: f.fileData.name, Err: errors.New("file handle is read only")}
|
|
||||||
}
|
|
||||||
n = len(b)
|
|
||||||
cur := atomic.LoadInt64(&f.at)
|
|
||||||
f.fileData.Lock()
|
|
||||||
defer f.fileData.Unlock()
|
|
||||||
diff := cur - int64(len(f.fileData.data))
|
|
||||||
var tail []byte
|
|
||||||
if n+int(cur) < len(f.fileData.data) {
|
|
||||||
tail = f.fileData.data[n+int(cur):]
|
|
||||||
}
|
|
||||||
if diff > 0 {
|
|
||||||
f.fileData.data = append(bytes.Repeat([]byte{00}, int(diff)), b...)
|
|
||||||
f.fileData.data = append(f.fileData.data, tail...)
|
|
||||||
} else {
|
|
||||||
f.fileData.data = append(f.fileData.data[:cur], b...)
|
|
||||||
f.fileData.data = append(f.fileData.data, tail...)
|
|
||||||
}
|
|
||||||
setModTime(f.fileData, time.Now())
|
|
||||||
|
|
||||||
atomic.StoreInt64(&f.at, int64(len(f.fileData.data)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *File) WriteAt(b []byte, off int64) (n int, err error) {
|
|
||||||
atomic.StoreInt64(&f.at, off)
|
|
||||||
return f.Write(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *File) WriteString(s string) (ret int, err error) {
|
|
||||||
return f.Write([]byte(s))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *File) Info() *FileInfo {
|
|
||||||
return &FileInfo{f.fileData}
|
|
||||||
}
|
|
||||||
|
|
||||||
type FileInfo struct {
|
|
||||||
*FileData
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implements os.FileInfo
|
|
||||||
func (s *FileInfo) Name() string {
|
|
||||||
s.Lock()
|
|
||||||
_, name := filepath.Split(s.name)
|
|
||||||
s.Unlock()
|
|
||||||
return name
|
|
||||||
}
|
|
||||||
func (s *FileInfo) Mode() os.FileMode {
|
|
||||||
s.Lock()
|
|
||||||
defer s.Unlock()
|
|
||||||
return s.mode
|
|
||||||
}
|
|
||||||
func (s *FileInfo) ModTime() time.Time {
|
|
||||||
s.Lock()
|
|
||||||
defer s.Unlock()
|
|
||||||
return s.modtime
|
|
||||||
}
|
|
||||||
func (s *FileInfo) IsDir() bool {
|
|
||||||
s.Lock()
|
|
||||||
defer s.Unlock()
|
|
||||||
return s.dir
|
|
||||||
}
|
|
||||||
func (s *FileInfo) Sys() interface{} { return nil }
|
|
||||||
func (s *FileInfo) Size() int64 {
|
|
||||||
if s.IsDir() {
|
|
||||||
return int64(42)
|
|
||||||
}
|
|
||||||
s.Lock()
|
|
||||||
defer s.Unlock()
|
|
||||||
return int64(len(s.data))
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrFileClosed = errors.New("File is closed")
|
|
||||||
ErrOutOfRange = errors.New("Out of range")
|
|
||||||
ErrTooLarge = errors.New("Too large")
|
|
||||||
ErrFileNotFound = os.ErrNotExist
|
|
||||||
ErrFileExists = os.ErrExist
|
|
||||||
ErrDestinationExists = os.ErrExist
|
|
||||||
)
|
|
365
vendor/github.com/spf13/afero/memmap.go
generated
vendored
365
vendor/github.com/spf13/afero/memmap.go
generated
vendored
@ -1,365 +0,0 @@
|
|||||||
// Copyright © 2014 Steve Francia <spf@spf13.com>.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package afero
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/spf13/afero/mem"
|
|
||||||
)
|
|
||||||
|
|
||||||
type MemMapFs struct {
|
|
||||||
mu sync.RWMutex
|
|
||||||
data map[string]*mem.FileData
|
|
||||||
init sync.Once
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewMemMapFs() Fs {
|
|
||||||
return &MemMapFs{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MemMapFs) getData() map[string]*mem.FileData {
|
|
||||||
m.init.Do(func() {
|
|
||||||
m.data = make(map[string]*mem.FileData)
|
|
||||||
// Root should always exist, right?
|
|
||||||
// TODO: what about windows?
|
|
||||||
m.data[FilePathSeparator] = mem.CreateDir(FilePathSeparator)
|
|
||||||
})
|
|
||||||
return m.data
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*MemMapFs) Name() string { return "MemMapFS" }
|
|
||||||
|
|
||||||
func (m *MemMapFs) Create(name string) (File, error) {
|
|
||||||
name = normalizePath(name)
|
|
||||||
m.mu.Lock()
|
|
||||||
file := mem.CreateFile(name)
|
|
||||||
m.getData()[name] = file
|
|
||||||
m.registerWithParent(file)
|
|
||||||
m.mu.Unlock()
|
|
||||||
return mem.NewFileHandle(file), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MemMapFs) unRegisterWithParent(fileName string) error {
|
|
||||||
f, err := m.lockfreeOpen(fileName)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
parent := m.findParent(f)
|
|
||||||
if parent == nil {
|
|
||||||
log.Panic("parent of ", f.Name(), " is nil")
|
|
||||||
}
|
|
||||||
|
|
||||||
parent.Lock()
|
|
||||||
mem.RemoveFromMemDir(parent, f)
|
|
||||||
parent.Unlock()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MemMapFs) findParent(f *mem.FileData) *mem.FileData {
|
|
||||||
pdir, _ := filepath.Split(f.Name())
|
|
||||||
pdir = filepath.Clean(pdir)
|
|
||||||
pfile, err := m.lockfreeOpen(pdir)
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return pfile
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MemMapFs) registerWithParent(f *mem.FileData) {
|
|
||||||
if f == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
parent := m.findParent(f)
|
|
||||||
if parent == nil {
|
|
||||||
pdir := filepath.Dir(filepath.Clean(f.Name()))
|
|
||||||
err := m.lockfreeMkdir(pdir, 0777)
|
|
||||||
if err != nil {
|
|
||||||
//log.Println("Mkdir error:", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
parent, err = m.lockfreeOpen(pdir)
|
|
||||||
if err != nil {
|
|
||||||
//log.Println("Open after Mkdir error:", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
parent.Lock()
|
|
||||||
mem.InitializeDir(parent)
|
|
||||||
mem.AddToMemDir(parent, f)
|
|
||||||
parent.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MemMapFs) lockfreeMkdir(name string, perm os.FileMode) error {
|
|
||||||
name = normalizePath(name)
|
|
||||||
x, ok := m.getData()[name]
|
|
||||||
if ok {
|
|
||||||
// Only return ErrFileExists if it's a file, not a directory.
|
|
||||||
i := mem.FileInfo{FileData: x}
|
|
||||||
if !i.IsDir() {
|
|
||||||
return ErrFileExists
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
item := mem.CreateDir(name)
|
|
||||||
m.getData()[name] = item
|
|
||||||
m.registerWithParent(item)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MemMapFs) Mkdir(name string, perm os.FileMode) error {
|
|
||||||
name = normalizePath(name)
|
|
||||||
|
|
||||||
m.mu.RLock()
|
|
||||||
_, ok := m.getData()[name]
|
|
||||||
m.mu.RUnlock()
|
|
||||||
if ok {
|
|
||||||
return &os.PathError{Op: "mkdir", Path: name, Err: ErrFileExists}
|
|
||||||
}
|
|
||||||
|
|
||||||
m.mu.Lock()
|
|
||||||
item := mem.CreateDir(name)
|
|
||||||
m.getData()[name] = item
|
|
||||||
m.registerWithParent(item)
|
|
||||||
m.mu.Unlock()
|
|
||||||
|
|
||||||
m.Chmod(name, perm|os.ModeDir)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MemMapFs) MkdirAll(path string, perm os.FileMode) error {
|
|
||||||
err := m.Mkdir(path, perm)
|
|
||||||
if err != nil {
|
|
||||||
if err.(*os.PathError).Err == ErrFileExists {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle some relative paths
|
|
||||||
func normalizePath(path string) string {
|
|
||||||
path = filepath.Clean(path)
|
|
||||||
|
|
||||||
switch path {
|
|
||||||
case ".":
|
|
||||||
return FilePathSeparator
|
|
||||||
case "..":
|
|
||||||
return FilePathSeparator
|
|
||||||
default:
|
|
||||||
return path
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MemMapFs) Open(name string) (File, error) {
|
|
||||||
f, err := m.open(name)
|
|
||||||
if f != nil {
|
|
||||||
return mem.NewReadOnlyFileHandle(f), err
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MemMapFs) openWrite(name string) (File, error) {
|
|
||||||
f, err := m.open(name)
|
|
||||||
if f != nil {
|
|
||||||
return mem.NewFileHandle(f), err
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MemMapFs) open(name string) (*mem.FileData, error) {
|
|
||||||
name = normalizePath(name)
|
|
||||||
|
|
||||||
m.mu.RLock()
|
|
||||||
f, ok := m.getData()[name]
|
|
||||||
m.mu.RUnlock()
|
|
||||||
if !ok {
|
|
||||||
return nil, &os.PathError{Op: "open", Path: name, Err: ErrFileNotFound}
|
|
||||||
}
|
|
||||||
return f, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MemMapFs) lockfreeOpen(name string) (*mem.FileData, error) {
|
|
||||||
name = normalizePath(name)
|
|
||||||
f, ok := m.getData()[name]
|
|
||||||
if ok {
|
|
||||||
return f, nil
|
|
||||||
} else {
|
|
||||||
return nil, ErrFileNotFound
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MemMapFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
|
|
||||||
chmod := false
|
|
||||||
file, err := m.openWrite(name)
|
|
||||||
if os.IsNotExist(err) && (flag&os.O_CREATE > 0) {
|
|
||||||
file, err = m.Create(name)
|
|
||||||
chmod = true
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if flag == os.O_RDONLY {
|
|
||||||
file = mem.NewReadOnlyFileHandle(file.(*mem.File).Data())
|
|
||||||
}
|
|
||||||
if flag&os.O_APPEND > 0 {
|
|
||||||
_, err = file.Seek(0, os.SEEK_END)
|
|
||||||
if err != nil {
|
|
||||||
file.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if flag&os.O_TRUNC > 0 && flag&(os.O_RDWR|os.O_WRONLY) > 0 {
|
|
||||||
err = file.Truncate(0)
|
|
||||||
if err != nil {
|
|
||||||
file.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if chmod {
|
|
||||||
m.Chmod(name, perm)
|
|
||||||
}
|
|
||||||
return file, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MemMapFs) Remove(name string) error {
|
|
||||||
name = normalizePath(name)
|
|
||||||
|
|
||||||
m.mu.Lock()
|
|
||||||
defer m.mu.Unlock()
|
|
||||||
|
|
||||||
if _, ok := m.getData()[name]; ok {
|
|
||||||
err := m.unRegisterWithParent(name)
|
|
||||||
if err != nil {
|
|
||||||
return &os.PathError{Op: "remove", Path: name, Err: err}
|
|
||||||
}
|
|
||||||
delete(m.getData(), name)
|
|
||||||
} else {
|
|
||||||
return &os.PathError{Op: "remove", Path: name, Err: os.ErrNotExist}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MemMapFs) RemoveAll(path string) error {
|
|
||||||
path = normalizePath(path)
|
|
||||||
m.mu.Lock()
|
|
||||||
m.unRegisterWithParent(path)
|
|
||||||
m.mu.Unlock()
|
|
||||||
|
|
||||||
m.mu.RLock()
|
|
||||||
defer m.mu.RUnlock()
|
|
||||||
|
|
||||||
for p, _ := range m.getData() {
|
|
||||||
if strings.HasPrefix(p, path) {
|
|
||||||
m.mu.RUnlock()
|
|
||||||
m.mu.Lock()
|
|
||||||
delete(m.getData(), p)
|
|
||||||
m.mu.Unlock()
|
|
||||||
m.mu.RLock()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MemMapFs) Rename(oldname, newname string) error {
|
|
||||||
oldname = normalizePath(oldname)
|
|
||||||
newname = normalizePath(newname)
|
|
||||||
|
|
||||||
if oldname == newname {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
m.mu.RLock()
|
|
||||||
defer m.mu.RUnlock()
|
|
||||||
if _, ok := m.getData()[oldname]; ok {
|
|
||||||
m.mu.RUnlock()
|
|
||||||
m.mu.Lock()
|
|
||||||
m.unRegisterWithParent(oldname)
|
|
||||||
fileData := m.getData()[oldname]
|
|
||||||
delete(m.getData(), oldname)
|
|
||||||
mem.ChangeFileName(fileData, newname)
|
|
||||||
m.getData()[newname] = fileData
|
|
||||||
m.registerWithParent(fileData)
|
|
||||||
m.mu.Unlock()
|
|
||||||
m.mu.RLock()
|
|
||||||
} else {
|
|
||||||
return &os.PathError{Op: "rename", Path: oldname, Err: ErrFileNotFound}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MemMapFs) Stat(name string) (os.FileInfo, error) {
|
|
||||||
f, err := m.Open(name)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
fi := mem.GetFileInfo(f.(*mem.File).Data())
|
|
||||||
return fi, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MemMapFs) Chmod(name string, mode os.FileMode) error {
|
|
||||||
name = normalizePath(name)
|
|
||||||
|
|
||||||
m.mu.RLock()
|
|
||||||
f, ok := m.getData()[name]
|
|
||||||
m.mu.RUnlock()
|
|
||||||
if !ok {
|
|
||||||
return &os.PathError{Op: "chmod", Path: name, Err: ErrFileNotFound}
|
|
||||||
}
|
|
||||||
|
|
||||||
m.mu.Lock()
|
|
||||||
mem.SetMode(f, mode)
|
|
||||||
m.mu.Unlock()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MemMapFs) Chtimes(name string, atime time.Time, mtime time.Time) error {
|
|
||||||
name = normalizePath(name)
|
|
||||||
|
|
||||||
m.mu.RLock()
|
|
||||||
f, ok := m.getData()[name]
|
|
||||||
m.mu.RUnlock()
|
|
||||||
if !ok {
|
|
||||||
return &os.PathError{Op: "chtimes", Path: name, Err: ErrFileNotFound}
|
|
||||||
}
|
|
||||||
|
|
||||||
m.mu.Lock()
|
|
||||||
mem.SetModTime(f, mtime)
|
|
||||||
m.mu.Unlock()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MemMapFs) List() {
|
|
||||||
for _, x := range m.data {
|
|
||||||
y := mem.FileInfo{FileData: x}
|
|
||||||
fmt.Println(x.Name(), y.Size())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// func debugMemMapList(fs Fs) {
|
|
||||||
// if x, ok := fs.(*MemMapFs); ok {
|
|
||||||
// x.List()
|
|
||||||
// }
|
|
||||||
// }
|
|
101
vendor/github.com/spf13/afero/os.go
generated
vendored
101
vendor/github.com/spf13/afero/os.go
generated
vendored
@ -1,101 +0,0 @@
|
|||||||
// Copyright © 2014 Steve Francia <spf@spf13.com>.
|
|
||||||
// Copyright 2013 tsuru authors. All rights reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package afero
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var _ Lstater = (*OsFs)(nil)
|
|
||||||
|
|
||||||
// OsFs is a Fs implementation that uses functions provided by the os package.
|
|
||||||
//
|
|
||||||
// For details in any method, check the documentation of the os package
|
|
||||||
// (http://golang.org/pkg/os/).
|
|
||||||
type OsFs struct{}
|
|
||||||
|
|
||||||
func NewOsFs() Fs {
|
|
||||||
return &OsFs{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (OsFs) Name() string { return "OsFs" }
|
|
||||||
|
|
||||||
func (OsFs) Create(name string) (File, error) {
|
|
||||||
f, e := os.Create(name)
|
|
||||||
if f == nil {
|
|
||||||
// while this looks strange, we need to return a bare nil (of type nil) not
|
|
||||||
// a nil value of type *os.File or nil won't be nil
|
|
||||||
return nil, e
|
|
||||||
}
|
|
||||||
return f, e
|
|
||||||
}
|
|
||||||
|
|
||||||
func (OsFs) Mkdir(name string, perm os.FileMode) error {
|
|
||||||
return os.Mkdir(name, perm)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (OsFs) MkdirAll(path string, perm os.FileMode) error {
|
|
||||||
return os.MkdirAll(path, perm)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (OsFs) Open(name string) (File, error) {
|
|
||||||
f, e := os.Open(name)
|
|
||||||
if f == nil {
|
|
||||||
// while this looks strange, we need to return a bare nil (of type nil) not
|
|
||||||
// a nil value of type *os.File or nil won't be nil
|
|
||||||
return nil, e
|
|
||||||
}
|
|
||||||
return f, e
|
|
||||||
}
|
|
||||||
|
|
||||||
func (OsFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
|
|
||||||
f, e := os.OpenFile(name, flag, perm)
|
|
||||||
if f == nil {
|
|
||||||
// while this looks strange, we need to return a bare nil (of type nil) not
|
|
||||||
// a nil value of type *os.File or nil won't be nil
|
|
||||||
return nil, e
|
|
||||||
}
|
|
||||||
return f, e
|
|
||||||
}
|
|
||||||
|
|
||||||
func (OsFs) Remove(name string) error {
|
|
||||||
return os.Remove(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (OsFs) RemoveAll(path string) error {
|
|
||||||
return os.RemoveAll(path)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (OsFs) Rename(oldname, newname string) error {
|
|
||||||
return os.Rename(oldname, newname)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (OsFs) Stat(name string) (os.FileInfo, error) {
|
|
||||||
return os.Stat(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (OsFs) Chmod(name string, mode os.FileMode) error {
|
|
||||||
return os.Chmod(name, mode)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (OsFs) Chtimes(name string, atime time.Time, mtime time.Time) error {
|
|
||||||
return os.Chtimes(name, atime, mtime)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (OsFs) LstatIfPossible(name string) (os.FileInfo, bool, error) {
|
|
||||||
fi, err := os.Lstat(name)
|
|
||||||
return fi, true, err
|
|
||||||
}
|
|
106
vendor/github.com/spf13/afero/path.go
generated
vendored
106
vendor/github.com/spf13/afero/path.go
generated
vendored
@ -1,106 +0,0 @@
|
|||||||
// Copyright ©2015 The Go Authors
|
|
||||||
// Copyright ©2015 Steve Francia <spf@spf13.com>
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package afero
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"sort"
|
|
||||||
)
|
|
||||||
|
|
||||||
// readDirNames reads the directory named by dirname and returns
|
|
||||||
// a sorted list of directory entries.
|
|
||||||
// adapted from https://golang.org/src/path/filepath/path.go
|
|
||||||
func readDirNames(fs Fs, dirname string) ([]string, error) {
|
|
||||||
f, err := fs.Open(dirname)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
names, err := f.Readdirnames(-1)
|
|
||||||
f.Close()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
sort.Strings(names)
|
|
||||||
return names, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// walk recursively descends path, calling walkFn
|
|
||||||
// adapted from https://golang.org/src/path/filepath/path.go
|
|
||||||
func walk(fs Fs, path string, info os.FileInfo, walkFn filepath.WalkFunc) error {
|
|
||||||
err := walkFn(path, info, nil)
|
|
||||||
if err != nil {
|
|
||||||
if info.IsDir() && err == filepath.SkipDir {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if !info.IsDir() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
names, err := readDirNames(fs, path)
|
|
||||||
if err != nil {
|
|
||||||
return walkFn(path, info, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, name := range names {
|
|
||||||
filename := filepath.Join(path, name)
|
|
||||||
fileInfo, err := lstatIfPossible(fs, filename)
|
|
||||||
if err != nil {
|
|
||||||
if err := walkFn(filename, fileInfo, err); err != nil && err != filepath.SkipDir {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err = walk(fs, filename, fileInfo, walkFn)
|
|
||||||
if err != nil {
|
|
||||||
if !fileInfo.IsDir() || err != filepath.SkipDir {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// if the filesystem supports it, use Lstat, else use fs.Stat
|
|
||||||
func lstatIfPossible(fs Fs, path string) (os.FileInfo, error) {
|
|
||||||
if lfs, ok := fs.(Lstater); ok {
|
|
||||||
fi, _, err := lfs.LstatIfPossible(path)
|
|
||||||
return fi, err
|
|
||||||
}
|
|
||||||
return fs.Stat(path)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Walk walks the file tree rooted at root, calling walkFn for each file or
|
|
||||||
// directory in the tree, including root. All errors that arise visiting files
|
|
||||||
// and directories are filtered by walkFn. The files are walked in lexical
|
|
||||||
// order, which makes the output deterministic but means that for very
|
|
||||||
// large directories Walk can be inefficient.
|
|
||||||
// Walk does not follow symbolic links.
|
|
||||||
|
|
||||||
func (a Afero) Walk(root string, walkFn filepath.WalkFunc) error {
|
|
||||||
return Walk(a.Fs, root, walkFn)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Walk(fs Fs, root string, walkFn filepath.WalkFunc) error {
|
|
||||||
info, err := lstatIfPossible(fs, root)
|
|
||||||
if err != nil {
|
|
||||||
return walkFn(root, nil, err)
|
|
||||||
}
|
|
||||||
return walk(fs, root, info, walkFn)
|
|
||||||
}
|
|
80
vendor/github.com/spf13/afero/readonlyfs.go
generated
vendored
80
vendor/github.com/spf13/afero/readonlyfs.go
generated
vendored
@ -1,80 +0,0 @@
|
|||||||
package afero
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"syscall"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var _ Lstater = (*ReadOnlyFs)(nil)
|
|
||||||
|
|
||||||
type ReadOnlyFs struct {
|
|
||||||
source Fs
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewReadOnlyFs(source Fs) Fs {
|
|
||||||
return &ReadOnlyFs{source: source}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *ReadOnlyFs) ReadDir(name string) ([]os.FileInfo, error) {
|
|
||||||
return ReadDir(r.source, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *ReadOnlyFs) Chtimes(n string, a, m time.Time) error {
|
|
||||||
return syscall.EPERM
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *ReadOnlyFs) Chmod(n string, m os.FileMode) error {
|
|
||||||
return syscall.EPERM
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *ReadOnlyFs) Name() string {
|
|
||||||
return "ReadOnlyFilter"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *ReadOnlyFs) Stat(name string) (os.FileInfo, error) {
|
|
||||||
return r.source.Stat(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *ReadOnlyFs) LstatIfPossible(name string) (os.FileInfo, bool, error) {
|
|
||||||
if lsf, ok := r.source.(Lstater); ok {
|
|
||||||
return lsf.LstatIfPossible(name)
|
|
||||||
}
|
|
||||||
fi, err := r.Stat(name)
|
|
||||||
return fi, false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *ReadOnlyFs) Rename(o, n string) error {
|
|
||||||
return syscall.EPERM
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *ReadOnlyFs) RemoveAll(p string) error {
|
|
||||||
return syscall.EPERM
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *ReadOnlyFs) Remove(n string) error {
|
|
||||||
return syscall.EPERM
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *ReadOnlyFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
|
|
||||||
if flag&(os.O_WRONLY|syscall.O_RDWR|os.O_APPEND|os.O_CREATE|os.O_TRUNC) != 0 {
|
|
||||||
return nil, syscall.EPERM
|
|
||||||
}
|
|
||||||
return r.source.OpenFile(name, flag, perm)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *ReadOnlyFs) Open(n string) (File, error) {
|
|
||||||
return r.source.Open(n)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *ReadOnlyFs) Mkdir(n string, p os.FileMode) error {
|
|
||||||
return syscall.EPERM
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *ReadOnlyFs) MkdirAll(n string, p os.FileMode) error {
|
|
||||||
return syscall.EPERM
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *ReadOnlyFs) Create(n string) (File, error) {
|
|
||||||
return nil, syscall.EPERM
|
|
||||||
}
|
|
214
vendor/github.com/spf13/afero/regexpfs.go
generated
vendored
214
vendor/github.com/spf13/afero/regexpfs.go
generated
vendored
@ -1,214 +0,0 @@
|
|||||||
package afero
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"regexp"
|
|
||||||
"syscall"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// The RegexpFs filters files (not directories) by regular expression. Only
|
|
||||||
// files matching the given regexp will be allowed, all others get a ENOENT error (
|
|
||||||
// "No such file or directory").
|
|
||||||
//
|
|
||||||
type RegexpFs struct {
|
|
||||||
re *regexp.Regexp
|
|
||||||
source Fs
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewRegexpFs(source Fs, re *regexp.Regexp) Fs {
|
|
||||||
return &RegexpFs{source: source, re: re}
|
|
||||||
}
|
|
||||||
|
|
||||||
type RegexpFile struct {
|
|
||||||
f File
|
|
||||||
re *regexp.Regexp
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *RegexpFs) matchesName(name string) error {
|
|
||||||
if r.re == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if r.re.MatchString(name) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return syscall.ENOENT
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *RegexpFs) dirOrMatches(name string) error {
|
|
||||||
dir, err := IsDir(r.source, name)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if dir {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return r.matchesName(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *RegexpFs) Chtimes(name string, a, m time.Time) error {
|
|
||||||
if err := r.dirOrMatches(name); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return r.source.Chtimes(name, a, m)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *RegexpFs) Chmod(name string, mode os.FileMode) error {
|
|
||||||
if err := r.dirOrMatches(name); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return r.source.Chmod(name, mode)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *RegexpFs) Name() string {
|
|
||||||
return "RegexpFs"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *RegexpFs) Stat(name string) (os.FileInfo, error) {
|
|
||||||
if err := r.dirOrMatches(name); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return r.source.Stat(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *RegexpFs) Rename(oldname, newname string) error {
|
|
||||||
dir, err := IsDir(r.source, oldname)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if dir {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if err := r.matchesName(oldname); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := r.matchesName(newname); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return r.source.Rename(oldname, newname)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *RegexpFs) RemoveAll(p string) error {
|
|
||||||
dir, err := IsDir(r.source, p)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !dir {
|
|
||||||
if err := r.matchesName(p); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return r.source.RemoveAll(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *RegexpFs) Remove(name string) error {
|
|
||||||
if err := r.dirOrMatches(name); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return r.source.Remove(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *RegexpFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
|
|
||||||
if err := r.dirOrMatches(name); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return r.source.OpenFile(name, flag, perm)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *RegexpFs) Open(name string) (File, error) {
|
|
||||||
dir, err := IsDir(r.source, name)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if !dir {
|
|
||||||
if err := r.matchesName(name); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
f, err := r.source.Open(name)
|
|
||||||
return &RegexpFile{f: f, re: r.re}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *RegexpFs) Mkdir(n string, p os.FileMode) error {
|
|
||||||
return r.source.Mkdir(n, p)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *RegexpFs) MkdirAll(n string, p os.FileMode) error {
|
|
||||||
return r.source.MkdirAll(n, p)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *RegexpFs) Create(name string) (File, error) {
|
|
||||||
if err := r.matchesName(name); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return r.source.Create(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *RegexpFile) Close() error {
|
|
||||||
return f.f.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *RegexpFile) Read(s []byte) (int, error) {
|
|
||||||
return f.f.Read(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *RegexpFile) ReadAt(s []byte, o int64) (int, error) {
|
|
||||||
return f.f.ReadAt(s, o)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *RegexpFile) Seek(o int64, w int) (int64, error) {
|
|
||||||
return f.f.Seek(o, w)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *RegexpFile) Write(s []byte) (int, error) {
|
|
||||||
return f.f.Write(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *RegexpFile) WriteAt(s []byte, o int64) (int, error) {
|
|
||||||
return f.f.WriteAt(s, o)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *RegexpFile) Name() string {
|
|
||||||
return f.f.Name()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *RegexpFile) Readdir(c int) (fi []os.FileInfo, err error) {
|
|
||||||
var rfi []os.FileInfo
|
|
||||||
rfi, err = f.f.Readdir(c)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
for _, i := range rfi {
|
|
||||||
if i.IsDir() || f.re.MatchString(i.Name()) {
|
|
||||||
fi = append(fi, i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return fi, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *RegexpFile) Readdirnames(c int) (n []string, err error) {
|
|
||||||
fi, err := f.Readdir(c)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
for _, s := range fi {
|
|
||||||
n = append(n, s.Name())
|
|
||||||
}
|
|
||||||
return n, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *RegexpFile) Stat() (os.FileInfo, error) {
|
|
||||||
return f.f.Stat()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *RegexpFile) Sync() error {
|
|
||||||
return f.f.Sync()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *RegexpFile) Truncate(s int64) error {
|
|
||||||
return f.f.Truncate(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *RegexpFile) WriteString(s string) (int, error) {
|
|
||||||
return f.f.WriteString(s)
|
|
||||||
}
|
|
320
vendor/github.com/spf13/afero/unionFile.go
generated
vendored
320
vendor/github.com/spf13/afero/unionFile.go
generated
vendored
@ -1,320 +0,0 @@
|
|||||||
package afero
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"syscall"
|
|
||||||
)
|
|
||||||
|
|
||||||
// The UnionFile implements the afero.File interface and will be returned
|
|
||||||
// when reading a directory present at least in the overlay or opening a file
|
|
||||||
// for writing.
|
|
||||||
//
|
|
||||||
// The calls to
|
|
||||||
// Readdir() and Readdirnames() merge the file os.FileInfo / names from the
|
|
||||||
// base and the overlay - for files present in both layers, only those
|
|
||||||
// from the overlay will be used.
|
|
||||||
//
|
|
||||||
// When opening files for writing (Create() / OpenFile() with the right flags)
|
|
||||||
// the operations will be done in both layers, starting with the overlay. A
|
|
||||||
// successful read in the overlay will move the cursor position in the base layer
|
|
||||||
// by the number of bytes read.
|
|
||||||
type UnionFile struct {
|
|
||||||
Base File
|
|
||||||
Layer File
|
|
||||||
Merger DirsMerger
|
|
||||||
off int
|
|
||||||
files []os.FileInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *UnionFile) Close() error {
|
|
||||||
// first close base, so we have a newer timestamp in the overlay. If we'd close
|
|
||||||
// the overlay first, we'd get a cacheStale the next time we access this file
|
|
||||||
// -> cache would be useless ;-)
|
|
||||||
if f.Base != nil {
|
|
||||||
f.Base.Close()
|
|
||||||
}
|
|
||||||
if f.Layer != nil {
|
|
||||||
return f.Layer.Close()
|
|
||||||
}
|
|
||||||
return BADFD
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *UnionFile) Read(s []byte) (int, error) {
|
|
||||||
if f.Layer != nil {
|
|
||||||
n, err := f.Layer.Read(s)
|
|
||||||
if (err == nil || err == io.EOF) && f.Base != nil {
|
|
||||||
// advance the file position also in the base file, the next
|
|
||||||
// call may be a write at this position (or a seek with SEEK_CUR)
|
|
||||||
if _, seekErr := f.Base.Seek(int64(n), os.SEEK_CUR); seekErr != nil {
|
|
||||||
// only overwrite err in case the seek fails: we need to
|
|
||||||
// report an eventual io.EOF to the caller
|
|
||||||
err = seekErr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
if f.Base != nil {
|
|
||||||
return f.Base.Read(s)
|
|
||||||
}
|
|
||||||
return 0, BADFD
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *UnionFile) ReadAt(s []byte, o int64) (int, error) {
|
|
||||||
if f.Layer != nil {
|
|
||||||
n, err := f.Layer.ReadAt(s, o)
|
|
||||||
if (err == nil || err == io.EOF) && f.Base != nil {
|
|
||||||
_, err = f.Base.Seek(o+int64(n), os.SEEK_SET)
|
|
||||||
}
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
if f.Base != nil {
|
|
||||||
return f.Base.ReadAt(s, o)
|
|
||||||
}
|
|
||||||
return 0, BADFD
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *UnionFile) Seek(o int64, w int) (pos int64, err error) {
|
|
||||||
if f.Layer != nil {
|
|
||||||
pos, err = f.Layer.Seek(o, w)
|
|
||||||
if (err == nil || err == io.EOF) && f.Base != nil {
|
|
||||||
_, err = f.Base.Seek(o, w)
|
|
||||||
}
|
|
||||||
return pos, err
|
|
||||||
}
|
|
||||||
if f.Base != nil {
|
|
||||||
return f.Base.Seek(o, w)
|
|
||||||
}
|
|
||||||
return 0, BADFD
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *UnionFile) Write(s []byte) (n int, err error) {
|
|
||||||
if f.Layer != nil {
|
|
||||||
n, err = f.Layer.Write(s)
|
|
||||||
if err == nil && f.Base != nil { // hmm, do we have fixed size files where a write may hit the EOF mark?
|
|
||||||
_, err = f.Base.Write(s)
|
|
||||||
}
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
if f.Base != nil {
|
|
||||||
return f.Base.Write(s)
|
|
||||||
}
|
|
||||||
return 0, BADFD
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *UnionFile) WriteAt(s []byte, o int64) (n int, err error) {
|
|
||||||
if f.Layer != nil {
|
|
||||||
n, err = f.Layer.WriteAt(s, o)
|
|
||||||
if err == nil && f.Base != nil {
|
|
||||||
_, err = f.Base.WriteAt(s, o)
|
|
||||||
}
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
if f.Base != nil {
|
|
||||||
return f.Base.WriteAt(s, o)
|
|
||||||
}
|
|
||||||
return 0, BADFD
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *UnionFile) Name() string {
|
|
||||||
if f.Layer != nil {
|
|
||||||
return f.Layer.Name()
|
|
||||||
}
|
|
||||||
return f.Base.Name()
|
|
||||||
}
|
|
||||||
|
|
||||||
// DirsMerger is how UnionFile weaves two directories together.
|
|
||||||
// It takes the FileInfo slices from the layer and the base and returns a
|
|
||||||
// single view.
|
|
||||||
type DirsMerger func(lofi, bofi []os.FileInfo) ([]os.FileInfo, error)
|
|
||||||
|
|
||||||
var defaultUnionMergeDirsFn = func(lofi, bofi []os.FileInfo) ([]os.FileInfo, error) {
|
|
||||||
var files = make(map[string]os.FileInfo)
|
|
||||||
|
|
||||||
for _, fi := range lofi {
|
|
||||||
files[fi.Name()] = fi
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, fi := range bofi {
|
|
||||||
if _, exists := files[fi.Name()]; !exists {
|
|
||||||
files[fi.Name()] = fi
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rfi := make([]os.FileInfo, len(files))
|
|
||||||
|
|
||||||
i := 0
|
|
||||||
for _, fi := range files {
|
|
||||||
rfi[i] = fi
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
|
|
||||||
return rfi, nil
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Readdir will weave the two directories together and
|
|
||||||
// return a single view of the overlayed directories.
|
|
||||||
// At the end of the directory view, the error is io.EOF if c > 0.
|
|
||||||
func (f *UnionFile) Readdir(c int) (ofi []os.FileInfo, err error) {
|
|
||||||
var merge DirsMerger = f.Merger
|
|
||||||
if merge == nil {
|
|
||||||
merge = defaultUnionMergeDirsFn
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.off == 0 {
|
|
||||||
var lfi []os.FileInfo
|
|
||||||
if f.Layer != nil {
|
|
||||||
lfi, err = f.Layer.Readdir(-1)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var bfi []os.FileInfo
|
|
||||||
if f.Base != nil {
|
|
||||||
bfi, err = f.Base.Readdir(-1)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
merged, err := merge(lfi, bfi)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
f.files = append(f.files, merged...)
|
|
||||||
}
|
|
||||||
|
|
||||||
if c <= 0 && len(f.files) == 0 {
|
|
||||||
return f.files, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.off >= len(f.files) {
|
|
||||||
return nil, io.EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
if c <= 0 {
|
|
||||||
return f.files[f.off:], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if c > len(f.files) {
|
|
||||||
c = len(f.files)
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() { f.off += c }()
|
|
||||||
return f.files[f.off:c], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *UnionFile) Readdirnames(c int) ([]string, error) {
|
|
||||||
rfi, err := f.Readdir(c)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
var names []string
|
|
||||||
for _, fi := range rfi {
|
|
||||||
names = append(names, fi.Name())
|
|
||||||
}
|
|
||||||
return names, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *UnionFile) Stat() (os.FileInfo, error) {
|
|
||||||
if f.Layer != nil {
|
|
||||||
return f.Layer.Stat()
|
|
||||||
}
|
|
||||||
if f.Base != nil {
|
|
||||||
return f.Base.Stat()
|
|
||||||
}
|
|
||||||
return nil, BADFD
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *UnionFile) Sync() (err error) {
|
|
||||||
if f.Layer != nil {
|
|
||||||
err = f.Layer.Sync()
|
|
||||||
if err == nil && f.Base != nil {
|
|
||||||
err = f.Base.Sync()
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if f.Base != nil {
|
|
||||||
return f.Base.Sync()
|
|
||||||
}
|
|
||||||
return BADFD
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *UnionFile) Truncate(s int64) (err error) {
|
|
||||||
if f.Layer != nil {
|
|
||||||
err = f.Layer.Truncate(s)
|
|
||||||
if err == nil && f.Base != nil {
|
|
||||||
err = f.Base.Truncate(s)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if f.Base != nil {
|
|
||||||
return f.Base.Truncate(s)
|
|
||||||
}
|
|
||||||
return BADFD
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *UnionFile) WriteString(s string) (n int, err error) {
|
|
||||||
if f.Layer != nil {
|
|
||||||
n, err = f.Layer.WriteString(s)
|
|
||||||
if err == nil && f.Base != nil {
|
|
||||||
_, err = f.Base.WriteString(s)
|
|
||||||
}
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
if f.Base != nil {
|
|
||||||
return f.Base.WriteString(s)
|
|
||||||
}
|
|
||||||
return 0, BADFD
|
|
||||||
}
|
|
||||||
|
|
||||||
func copyToLayer(base Fs, layer Fs, name string) error {
|
|
||||||
bfh, err := base.Open(name)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer bfh.Close()
|
|
||||||
|
|
||||||
// First make sure the directory exists
|
|
||||||
exists, err := Exists(layer, filepath.Dir(name))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !exists {
|
|
||||||
err = layer.MkdirAll(filepath.Dir(name), 0777) // FIXME?
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the file on the overlay
|
|
||||||
lfh, err := layer.Create(name)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
n, err := io.Copy(lfh, bfh)
|
|
||||||
if err != nil {
|
|
||||||
// If anything fails, clean up the file
|
|
||||||
layer.Remove(name)
|
|
||||||
lfh.Close()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
bfi, err := bfh.Stat()
|
|
||||||
if err != nil || bfi.Size() != n {
|
|
||||||
layer.Remove(name)
|
|
||||||
lfh.Close()
|
|
||||||
return syscall.EIO
|
|
||||||
}
|
|
||||||
|
|
||||||
err = lfh.Close()
|
|
||||||
if err != nil {
|
|
||||||
layer.Remove(name)
|
|
||||||
lfh.Close()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return layer.Chtimes(name, bfi.ModTime(), bfi.ModTime())
|
|
||||||
}
|
|
330
vendor/github.com/spf13/afero/util.go
generated
vendored
330
vendor/github.com/spf13/afero/util.go
generated
vendored
@ -1,330 +0,0 @@
|
|||||||
// Copyright ©2015 Steve Francia <spf@spf13.com>
|
|
||||||
// Portions Copyright ©2015 The Hugo Authors
|
|
||||||
// Portions Copyright 2016-present Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package afero
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"unicode"
|
|
||||||
|
|
||||||
"golang.org/x/text/transform"
|
|
||||||
"golang.org/x/text/unicode/norm"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Filepath separator defined by os.Separator.
|
|
||||||
const FilePathSeparator = string(filepath.Separator)
|
|
||||||
|
|
||||||
// Takes a reader and a path and writes the content
|
|
||||||
func (a Afero) WriteReader(path string, r io.Reader) (err error) {
|
|
||||||
return WriteReader(a.Fs, path, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
func WriteReader(fs Fs, path string, r io.Reader) (err error) {
|
|
||||||
dir, _ := filepath.Split(path)
|
|
||||||
ospath := filepath.FromSlash(dir)
|
|
||||||
|
|
||||||
if ospath != "" {
|
|
||||||
err = fs.MkdirAll(ospath, 0777) // rwx, rw, r
|
|
||||||
if err != nil {
|
|
||||||
if err != os.ErrExist {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
file, err := fs.Create(path)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer file.Close()
|
|
||||||
|
|
||||||
_, err = io.Copy(file, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Same as WriteReader but checks to see if file/directory already exists.
|
|
||||||
func (a Afero) SafeWriteReader(path string, r io.Reader) (err error) {
|
|
||||||
return SafeWriteReader(a.Fs, path, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
func SafeWriteReader(fs Fs, path string, r io.Reader) (err error) {
|
|
||||||
dir, _ := filepath.Split(path)
|
|
||||||
ospath := filepath.FromSlash(dir)
|
|
||||||
|
|
||||||
if ospath != "" {
|
|
||||||
err = fs.MkdirAll(ospath, 0777) // rwx, rw, r
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
exists, err := Exists(fs, path)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if exists {
|
|
||||||
return fmt.Errorf("%v already exists", path)
|
|
||||||
}
|
|
||||||
|
|
||||||
file, err := fs.Create(path)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer file.Close()
|
|
||||||
|
|
||||||
_, err = io.Copy(file, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a Afero) GetTempDir(subPath string) string {
|
|
||||||
return GetTempDir(a.Fs, subPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetTempDir returns the default temp directory with trailing slash
|
|
||||||
// if subPath is not empty then it will be created recursively with mode 777 rwx rwx rwx
|
|
||||||
func GetTempDir(fs Fs, subPath string) string {
|
|
||||||
addSlash := func(p string) string {
|
|
||||||
if FilePathSeparator != p[len(p)-1:] {
|
|
||||||
p = p + FilePathSeparator
|
|
||||||
}
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
dir := addSlash(os.TempDir())
|
|
||||||
|
|
||||||
if subPath != "" {
|
|
||||||
// preserve windows backslash :-(
|
|
||||||
if FilePathSeparator == "\\" {
|
|
||||||
subPath = strings.Replace(subPath, "\\", "____", -1)
|
|
||||||
}
|
|
||||||
dir = dir + UnicodeSanitize((subPath))
|
|
||||||
if FilePathSeparator == "\\" {
|
|
||||||
dir = strings.Replace(dir, "____", "\\", -1)
|
|
||||||
}
|
|
||||||
|
|
||||||
if exists, _ := Exists(fs, dir); exists {
|
|
||||||
return addSlash(dir)
|
|
||||||
}
|
|
||||||
|
|
||||||
err := fs.MkdirAll(dir, 0777)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
dir = addSlash(dir)
|
|
||||||
}
|
|
||||||
return dir
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rewrite string to remove non-standard path characters
|
|
||||||
func UnicodeSanitize(s string) string {
|
|
||||||
source := []rune(s)
|
|
||||||
target := make([]rune, 0, len(source))
|
|
||||||
|
|
||||||
for _, r := range source {
|
|
||||||
if unicode.IsLetter(r) ||
|
|
||||||
unicode.IsDigit(r) ||
|
|
||||||
unicode.IsMark(r) ||
|
|
||||||
r == '.' ||
|
|
||||||
r == '/' ||
|
|
||||||
r == '\\' ||
|
|
||||||
r == '_' ||
|
|
||||||
r == '-' ||
|
|
||||||
r == '%' ||
|
|
||||||
r == ' ' ||
|
|
||||||
r == '#' {
|
|
||||||
target = append(target, r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return string(target)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transform characters with accents into plain forms.
|
|
||||||
func NeuterAccents(s string) string {
|
|
||||||
t := transform.Chain(norm.NFD, transform.RemoveFunc(isMn), norm.NFC)
|
|
||||||
result, _, _ := transform.String(t, string(s))
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func isMn(r rune) bool {
|
|
||||||
return unicode.Is(unicode.Mn, r) // Mn: nonspacing marks
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a Afero) FileContainsBytes(filename string, subslice []byte) (bool, error) {
|
|
||||||
return FileContainsBytes(a.Fs, filename, subslice)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if a file contains a specified byte slice.
|
|
||||||
func FileContainsBytes(fs Fs, filename string, subslice []byte) (bool, error) {
|
|
||||||
f, err := fs.Open(filename)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
return readerContainsAny(f, subslice), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a Afero) FileContainsAnyBytes(filename string, subslices [][]byte) (bool, error) {
|
|
||||||
return FileContainsAnyBytes(a.Fs, filename, subslices)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if a file contains any of the specified byte slices.
|
|
||||||
func FileContainsAnyBytes(fs Fs, filename string, subslices [][]byte) (bool, error) {
|
|
||||||
f, err := fs.Open(filename)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
return readerContainsAny(f, subslices...), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// readerContains reports whether any of the subslices is within r.
|
|
||||||
func readerContainsAny(r io.Reader, subslices ...[]byte) bool {
|
|
||||||
|
|
||||||
if r == nil || len(subslices) == 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
largestSlice := 0
|
|
||||||
|
|
||||||
for _, sl := range subslices {
|
|
||||||
if len(sl) > largestSlice {
|
|
||||||
largestSlice = len(sl)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if largestSlice == 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
bufflen := largestSlice * 4
|
|
||||||
halflen := bufflen / 2
|
|
||||||
buff := make([]byte, bufflen)
|
|
||||||
var err error
|
|
||||||
var n, i int
|
|
||||||
|
|
||||||
for {
|
|
||||||
i++
|
|
||||||
if i == 1 {
|
|
||||||
n, err = io.ReadAtLeast(r, buff[:halflen], halflen)
|
|
||||||
} else {
|
|
||||||
if i != 2 {
|
|
||||||
// shift left to catch overlapping matches
|
|
||||||
copy(buff[:], buff[halflen:])
|
|
||||||
}
|
|
||||||
n, err = io.ReadAtLeast(r, buff[halflen:], halflen)
|
|
||||||
}
|
|
||||||
|
|
||||||
if n > 0 {
|
|
||||||
for _, sl := range subslices {
|
|
||||||
if bytes.Contains(buff, sl) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a Afero) DirExists(path string) (bool, error) {
|
|
||||||
return DirExists(a.Fs, path)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DirExists checks if a path exists and is a directory.
|
|
||||||
func DirExists(fs Fs, path string) (bool, error) {
|
|
||||||
fi, err := fs.Stat(path)
|
|
||||||
if err == nil && fi.IsDir() {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a Afero) IsDir(path string) (bool, error) {
|
|
||||||
return IsDir(a.Fs, path)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsDir checks if a given path is a directory.
|
|
||||||
func IsDir(fs Fs, path string) (bool, error) {
|
|
||||||
fi, err := fs.Stat(path)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
return fi.IsDir(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a Afero) IsEmpty(path string) (bool, error) {
|
|
||||||
return IsEmpty(a.Fs, path)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsEmpty checks if a given file or directory is empty.
|
|
||||||
func IsEmpty(fs Fs, path string) (bool, error) {
|
|
||||||
if b, _ := Exists(fs, path); !b {
|
|
||||||
return false, fmt.Errorf("%q path does not exist", path)
|
|
||||||
}
|
|
||||||
fi, err := fs.Stat(path)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
if fi.IsDir() {
|
|
||||||
f, err := fs.Open(path)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
list, err := f.Readdir(-1)
|
|
||||||
return len(list) == 0, nil
|
|
||||||
}
|
|
||||||
return fi.Size() == 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a Afero) Exists(path string) (bool, error) {
|
|
||||||
return Exists(a.Fs, path)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if a file or directory exists.
|
|
||||||
func Exists(fs Fs, path string) (bool, error) {
|
|
||||||
_, err := fs.Stat(path)
|
|
||||||
if err == nil {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func FullBaseFsPath(basePathFs *BasePathFs, relativePath string) string {
|
|
||||||
combinedPath := filepath.Join(basePathFs.path, relativePath)
|
|
||||||
if parent, ok := basePathFs.source.(*BasePathFs); ok {
|
|
||||||
return FullBaseFsPath(parent, combinedPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
return combinedPath
|
|
||||||
}
|
|
4
vendor/modules.txt
vendored
4
vendor/modules.txt
vendored
@ -706,10 +706,6 @@ github.com/shurcooL/sanitized_anchor_name
|
|||||||
github.com/sirupsen/logrus
|
github.com/sirupsen/logrus
|
||||||
# github.com/soheilhy/cmux v0.1.5 => github.com/soheilhy/cmux v0.1.5
|
# github.com/soheilhy/cmux v0.1.5 => github.com/soheilhy/cmux v0.1.5
|
||||||
github.com/soheilhy/cmux
|
github.com/soheilhy/cmux
|
||||||
# github.com/spf13/afero v1.2.2 => github.com/spf13/afero v1.2.2
|
|
||||||
## explicit
|
|
||||||
github.com/spf13/afero
|
|
||||||
github.com/spf13/afero/mem
|
|
||||||
# github.com/spf13/cobra v1.1.3 => github.com/spf13/cobra v1.1.3
|
# github.com/spf13/cobra v1.1.3 => github.com/spf13/cobra v1.1.3
|
||||||
## explicit
|
## explicit
|
||||||
github.com/spf13/cobra
|
github.com/spf13/cobra
|
||||||
|
Loading…
Reference in New Issue
Block a user