updating github.com/spf13/afero to v1.2.2

This commit is contained in:
Davanum Srinivas
2019-06-14 11:32:53 -04:00
parent caba257fc9
commit 56612c8b2e
55 changed files with 504 additions and 4585 deletions

View File

@@ -2,9 +2,8 @@ sudo: false
language: go
go:
- 1.5.4
- 1.6.3
- 1.7
- 1.9
- "1.10"
- tip
os:
@@ -17,5 +16,6 @@ matrix:
fast_finish: true
script:
- go test -v ./...
- go build
- go test -race -v ./...

View File

@@ -11,13 +11,13 @@ go_library(
"copyOnWriteFs.go",
"httpFs.go",
"ioutil.go",
"lstater.go",
"match.go",
"memmap.go",
"memradix.go",
"os.go",
"path.go",
"readonlyfs.go",
"regexpfs.go",
"sftp.go",
"unionFile.go",
"util.go",
],
@@ -25,9 +25,7 @@ go_library(
importpath = "github.com/spf13/afero",
visibility = ["//visibility:public"],
deps = [
"//vendor/github.com/pkg/sftp:go_default_library",
"//vendor/github.com/spf13/afero/mem:go_default_library",
"//vendor/github.com/spf13/afero/sftp:go_default_library",
"//vendor/golang.org/x/text/transform:go_default_library",
"//vendor/golang.org/x/text/unicode/norm:go_default_library",
],
@@ -45,7 +43,6 @@ filegroup(
srcs = [
":package-srcs",
"//vendor/github.com/spf13/afero/mem:all-srcs",
"//vendor/github.com/spf13/afero/sftp:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],

View File

@@ -61,11 +61,11 @@ import "github.com/spf13/afero"
First define a package variable and set it to a pointer to a filesystem.
```go
var AppFs afero.Fs = afero.NewMemMapFs()
var AppFs = afero.NewMemMapFs()
or
var AppFs afero.Fs = afero.NewOsFs()
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
@@ -81,7 +81,10 @@ So if my application before had:
```go
os.Open('/tmp/foo')
```
We would replace it with a call to `AppFs.Open('/tmp/foo')`.
We would replace it with:
```go
AppFs.Open('/tmp/foo')
```
`AppFs` being the variable we defined above.
@@ -166,8 +169,8 @@ f, err := afero.TempFile(fs,"", "ioutil-test")
### Calling via Afero
```go
fs := afero.NewMemMapFs
afs := &Afero{Fs: fs}
fs := afero.NewMemMapFs()
afs := &afero.Afero{Fs: fs}
f, err := afs.TempFile("", "ioutil-test")
```
@@ -198,17 +201,17 @@ 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()
appFS := afero.NewMemMapFs()
// create test files and directories
appFS.MkdirAll("src/a", 0755))
appFS.MkdirAll("src/a", 0755)
afero.WriteFile(appFS, "src/a/b", []byte("file b"), 0644)
afero.WriteFile(appFS, "src/c", []byte("file c"), 0644)
_, err := appFS.Stat("src/c")
name := "src/c"
_, err := appFS.Stat(name)
if os.IsNotExist(err) {
t.Errorf("file \"%s\" does not exist.\n", name)
t.Errorf("file \"%s\" does not exist.\n", name)
}
}
```
# Available Backends

View File

@@ -77,7 +77,7 @@ type Fs interface {
// happens.
Remove(name string) error
// RemoveAll removes a directory path and all any children it contains. It
// 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

View File

@@ -12,4 +12,4 @@ build_script:
go build github.com/spf13/afero
test_script:
- cmd: go test -v github.com/spf13/afero
- cmd: go test -race -v github.com/spf13/afero/...

View File

@@ -1,7 +1,6 @@
package afero
import (
"errors"
"os"
"path/filepath"
"runtime"
@@ -9,6 +8,8 @@ import (
"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.
@@ -22,6 +23,16 @@ type BasePathFs struct {
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}
}
@@ -30,7 +41,7 @@ func NewBasePathFs(source Fs, path string) Fs {
// 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 "", err
return name, err
}
bpath := filepath.Clean(b.path)
@@ -52,7 +63,7 @@ func validateBasePathName(name string) error {
// 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.PathError{"realPath", name, errors.New("got a real OS path instead of a virtual")}
return os.ErrNotExist
}
return nil
@@ -60,14 +71,14 @@ func validateBasePathName(name string) error {
func (b *BasePathFs) Chtimes(name string, atime, mtime time.Time) (err error) {
if name, err = b.RealPath(name); err != nil {
return &os.PathError{"chtimes", name, err}
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{"chmod", name, err}
return &os.PathError{Op: "chmod", Path: name, Err: err}
}
return b.source.Chmod(name, mode)
}
@@ -78,68 +89,92 @@ func (b *BasePathFs) Name() string {
func (b *BasePathFs) Stat(name string) (fi os.FileInfo, err error) {
if name, err = b.RealPath(name); err != nil {
return nil, &os.PathError{"stat", name, err}
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{"rename", oldname, err}
return &os.PathError{Op: "rename", Path: oldname, Err: err}
}
if newname, err = b.RealPath(newname); err != nil {
return &os.PathError{"rename", newname, err}
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{"remove_all", name, err}
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{"remove", name, err}
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{"openfile", name, err}
return nil, &os.PathError{Op: "openfile", Path: name, Err: err}
}
return b.source.OpenFile(name, flag, mode)
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{"open", name, err}
return nil, &os.PathError{Op: "open", Path: name, Err: err}
}
return b.source.Open(name)
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{"mkdir", name, err}
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{"mkdir", name, err}
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{"create", name, err}
return nil, &os.PathError{Op: "create", Path: name, Err: err}
}
return b.source.Create(name)
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

View File

@@ -32,9 +32,8 @@ func NewCacheOnReadFs(base Fs, layer Fs, cacheTime time.Duration) Fs {
type cacheState int
const (
cacheUnknown cacheState = iota
// not present in the overlay, unknown if it exists in the base:
cacheMiss
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,
@@ -65,15 +64,10 @@ func (u *CacheOnReadFs) cacheStatus(name string) (state cacheState, fi os.FileIn
return cacheHit, lfi, nil
}
if err == syscall.ENOENT {
if err == syscall.ENOENT || os.IsNotExist(err) {
return cacheMiss, nil, nil
}
var ok bool
if err, ok = err.(*os.PathError); ok {
if err == os.ErrNotExist {
return cacheMiss, nil, nil
}
}
return cacheMiss, nil, err
}
@@ -211,7 +205,7 @@ func (u *CacheOnReadFs) OpenFile(name string, flag int, perm os.FileMode) (File,
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 &UnionFile{Base: bfi, Layer: lfi}, nil
}
return u.layer.OpenFile(name, flag, perm)
}
@@ -257,7 +251,7 @@ func (u *CacheOnReadFs) Open(name string) (File, error) {
if err != nil && bfile == nil {
return nil, err
}
return &UnionFile{base: bfile, layer: lfile}, nil
return &UnionFile{Base: bfile, Layer: lfile}, nil
}
func (u *CacheOnReadFs) Mkdir(name string, perm os.FileMode) error {
@@ -292,5 +286,5 @@ func (u *CacheOnReadFs) Create(name string) (File, error) {
bfh.Close()
return nil, err
}
return &UnionFile{base: bfh, layer: lfh}, nil
return &UnionFile{Base: bfh, Layer: lfh}, nil
}

View File

@@ -8,6 +8,8 @@ import (
"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
@@ -76,18 +78,55 @@ func (u *CopyOnWriteFs) Chmod(name string, mode os.FileMode) error {
func (u *CopyOnWriteFs) Stat(name string) (os.FileInfo, error) {
fi, err := u.layer.Stat(name)
if err != nil {
origErr := err
if e, ok := err.(*os.PathError); ok {
err = e.Err
}
if err == syscall.ENOENT || err == syscall.ENOTDIR {
isNotExist := u.isNotExist(err)
if isNotExist {
return u.base.Stat(name)
}
return nil, origErr
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)
@@ -219,7 +258,7 @@ func (u *CopyOnWriteFs) Open(name string) (File, error) {
return nil, fmt.Errorf("BaseErr: %v\nOverlayErr: %v", bErr, lErr)
}
return &UnionFile{base: bfile, layer: lfile}, nil
return &UnionFile{Base: bfile, Layer: lfile}, nil
}
func (u *CopyOnWriteFs) Mkdir(name string, perm os.FileMode) error {
@@ -228,7 +267,7 @@ func (u *CopyOnWriteFs) Mkdir(name string, perm os.FileMode) error {
return u.layer.MkdirAll(name, perm)
}
if dir {
return syscall.EEXIST
return ErrFileExists
}
return u.layer.MkdirAll(name, perm)
}
@@ -243,7 +282,8 @@ func (u *CopyOnWriteFs) MkdirAll(name string, perm os.FileMode) error {
return u.layer.MkdirAll(name, perm)
}
if dir {
return syscall.EEXIST
// This is in line with how os.MkdirAll behaves.
return nil
}
return u.layer.MkdirAll(name, perm)
}

3
vendor/github.com/spf13/afero/go.mod generated vendored Normal file
View File

@@ -0,0 +1,3 @@
module github.com/spf13/afero
require golang.org/x/text v0.3.0

2
vendor/github.com/spf13/afero/go.sum generated vendored Normal file
View File

@@ -0,0 +1,2 @@
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=

View File

@@ -1,4 +1,4 @@
// Copyright © 2014 Steve Francia <spf@spf13.com>.
// 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.
@@ -12,3 +12,16 @@
// 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 Normal file
View File

@@ -0,0 +1,110 @@
// 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
}

View File

@@ -59,7 +59,9 @@ type FileData struct {
modtime time.Time
}
func (d FileData) Name() string {
func (d *FileData) Name() string {
d.Lock()
defer d.Unlock()
return d.name
}
@@ -72,14 +74,24 @@ func CreateDir(name string) *FileData {
}
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
}
@@ -100,14 +112,14 @@ func (f *File) Close() error {
f.fileData.Lock()
f.closed = true
if !f.readOnly {
SetModTime(f.fileData, time.Now())
setModTime(f.fileData, time.Now())
}
f.fileData.Unlock()
return nil
}
func (f *File) Name() string {
return f.fileData.name
return f.fileData.Name()
}
func (f *File) Stat() (os.FileInfo, error) {
@@ -119,6 +131,9 @@ func (f *File) Sync() error {
}
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()
@@ -164,6 +179,9 @@ func (f *File) Read(b []byte) (n int, err error) {
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 {
@@ -184,7 +202,7 @@ func (f *File) Truncate(size int64) error {
return ErrFileClosed
}
if f.readOnly {
return &os.PathError{"truncate", f.fileData.name, errors.New("file handle is read only")}
return &os.PathError{Op: "truncate", Path: f.fileData.name, Err: errors.New("file handle is read only")}
}
if size < 0 {
return ErrOutOfRange
@@ -195,7 +213,7 @@ func (f *File) Truncate(size int64) error {
} else {
f.fileData.data = f.fileData.data[0:size]
}
SetModTime(f.fileData, time.Now())
setModTime(f.fileData, time.Now())
return nil
}
@@ -216,7 +234,7 @@ func (f *File) Seek(offset int64, whence int) (int64, error) {
func (f *File) Write(b []byte) (n int, err error) {
if f.readOnly {
return 0, &os.PathError{"write", f.fileData.name, errors.New("file handle is read only")}
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)
@@ -234,7 +252,7 @@ func (f *File) Write(b []byte) (n int, err error) {
f.fileData.data = append(f.fileData.data[:cur], b...)
f.fileData.data = append(f.fileData.data, tail...)
}
SetModTime(f.fileData, time.Now())
setModTime(f.fileData, time.Now())
atomic.StoreInt64(&f.at, int64(len(f.fileData.data)))
return
@@ -259,17 +277,33 @@ type FileInfo struct {
// 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 { return s.mode }
func (s *FileInfo) ModTime() time.Time { return s.modtime }
func (s *FileInfo) IsDir() bool { return s.dir }
func (s *FileInfo) Sys() interface{} { return nil }
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))
}

View File

@@ -35,8 +35,6 @@ func NewMemMapFs() Fs {
return &MemMapFs{}
}
var memfsInit sync.Once
func (m *MemMapFs) getData() map[string]*mem.FileData {
m.init.Do(func() {
m.data = make(map[string]*mem.FileData)
@@ -47,7 +45,7 @@ func (m *MemMapFs) getData() map[string]*mem.FileData {
return m.data
}
func (MemMapFs) Name() string { return "MemMapFS" }
func (*MemMapFs) Name() string { return "MemMapFS" }
func (m *MemMapFs) Create(name string) (File, error) {
name = normalizePath(name)
@@ -68,7 +66,10 @@ func (m *MemMapFs) unRegisterWithParent(fileName string) error {
if parent == nil {
log.Panic("parent of ", f.Name(), " is nil")
}
parent.Lock()
mem.RemoveFromMemDir(parent, f)
parent.Unlock()
return nil
}
@@ -101,8 +102,10 @@ func (m *MemMapFs) registerWithParent(f *mem.FileData) {
}
}
parent.Lock()
mem.InitializeDir(parent)
mem.AddToMemDir(parent, f)
parent.Unlock()
}
func (m *MemMapFs) lockfreeMkdir(name string, perm os.FileMode) error {
@@ -110,7 +113,7 @@ func (m *MemMapFs) lockfreeMkdir(name string, perm os.FileMode) error {
x, ok := m.getData()[name]
if ok {
// Only return ErrFileExists if it's a file, not a directory.
i := mem.FileInfo{x}
i := mem.FileInfo{FileData: x}
if !i.IsDir() {
return ErrFileExists
}
@@ -129,14 +132,17 @@ func (m *MemMapFs) Mkdir(name string, perm os.FileMode) error {
_, ok := m.getData()[name]
m.mu.RUnlock()
if ok {
return &os.PathError{"mkdir", name, ErrFileExists}
} else {
m.mu.Lock()
item := mem.CreateDir(name)
m.getData()[name] = item
m.registerWithParent(item)
m.mu.Unlock()
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
}
@@ -145,9 +151,8 @@ func (m *MemMapFs) MkdirAll(path string, perm os.FileMode) error {
if err != nil {
if err.(*os.PathError).Err == ErrFileExists {
return nil
} else {
return err
}
return err
}
return nil
}
@@ -189,7 +194,7 @@ func (m *MemMapFs) open(name string) (*mem.FileData, error) {
f, ok := m.getData()[name]
m.mu.RUnlock()
if !ok {
return nil, &os.PathError{"open", name, ErrFileNotFound}
return nil, &os.PathError{Op: "open", Path: name, Err: ErrFileNotFound}
}
return f, nil
}
@@ -205,9 +210,11 @@ func (m *MemMapFs) lockfreeOpen(name string) (*mem.FileData, error) {
}
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
@@ -229,6 +236,9 @@ func (m *MemMapFs) OpenFile(name string, flag int, perm os.FileMode) (File, erro
return nil, err
}
}
if chmod {
m.Chmod(name, perm)
}
return file, nil
}
@@ -241,11 +251,11 @@ func (m *MemMapFs) Remove(name string) error {
if _, ok := m.getData()[name]; ok {
err := m.unRegisterWithParent(name)
if err != nil {
return &os.PathError{"remove", name, err}
return &os.PathError{Op: "remove", Path: name, Err: err}
}
delete(m.getData(), name)
} else {
return &os.PathError{"remove", name, os.ErrNotExist}
return &os.PathError{Op: "remove", Path: name, Err: os.ErrNotExist}
}
return nil
}
@@ -293,7 +303,7 @@ func (m *MemMapFs) Rename(oldname, newname string) error {
m.mu.Unlock()
m.mu.RLock()
} else {
return &os.PathError{"rename", oldname, ErrFileNotFound}
return &os.PathError{Op: "rename", Path: oldname, Err: ErrFileNotFound}
}
return nil
}
@@ -309,9 +319,12 @@ func (m *MemMapFs) Stat(name string) (os.FileInfo, error) {
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{"chmod", name, ErrFileNotFound}
return &os.PathError{Op: "chmod", Path: name, Err: ErrFileNotFound}
}
m.mu.Lock()
@@ -323,9 +336,12 @@ func (m *MemMapFs) Chmod(name string, mode os.FileMode) error {
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{"chtimes", name, ErrFileNotFound}
return &os.PathError{Op: "chtimes", Path: name, Err: ErrFileNotFound}
}
m.mu.Lock()
@@ -337,13 +353,13 @@ func (m *MemMapFs) Chtimes(name string, atime time.Time, mtime time.Time) error
func (m *MemMapFs) List() {
for _, x := range m.data {
y := mem.FileInfo{x}
y := mem.FileInfo{FileData: x}
fmt.Println(x.Name(), y.Size())
}
}
func debugMemMapList(fs Fs) {
if x, ok := fs.(*MemMapFs); ok {
x.List()
}
}
// func debugMemMapList(fs Fs) {
// if x, ok := fs.(*MemMapFs); ok {
// x.List()
// }
// }

View File

@@ -19,6 +19,8 @@ import (
"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
@@ -92,3 +94,8 @@ func (OsFs) Chmod(name string, mode os.FileMode) error {
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
}

View File

@@ -60,7 +60,7 @@ func walk(fs Fs, path string, info os.FileInfo, walkFn filepath.WalkFunc) error
for _, name := range names {
filename := filepath.Join(path, name)
fileInfo, err := lstatIfOs(fs, filename)
fileInfo, err := lstatIfPossible(fs, filename)
if err != nil {
if err := walkFn(filename, fileInfo, err); err != nil && err != filepath.SkipDir {
return err
@@ -77,15 +77,13 @@ func walk(fs Fs, path string, info os.FileInfo, walkFn filepath.WalkFunc) error
return nil
}
// if the filesystem is OsFs use Lstat, else use fs.Stat
func lstatIfOs(fs Fs, path string) (info os.FileInfo, err error) {
_, ok := fs.(*OsFs)
if ok {
info, err = os.Lstat(path)
} else {
info, err = fs.Stat(path)
// 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
return fs.Stat(path)
}
// Walk walks the file tree rooted at root, calling walkFn for each file or
@@ -100,7 +98,7 @@ func (a Afero) Walk(root string, walkFn filepath.WalkFunc) error {
}
func Walk(fs Fs, root string, walkFn filepath.WalkFunc) error {
info, err := lstatIfOs(fs, root)
info, err := lstatIfPossible(fs, root)
if err != nil {
return walkFn(root, nil, err)
}

View File

@@ -6,6 +6,8 @@ import (
"time"
)
var _ Lstater = (*ReadOnlyFs)(nil)
type ReadOnlyFs struct {
source Fs
}
@@ -34,6 +36,14 @@ 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
}

128
vendor/github.com/spf13/afero/sftp.go generated vendored
View File

@@ -1,128 +0,0 @@
// Copyright © 2015 Jerry Jacobs <jerry.jacobs@xor-gate.org>.
//
// 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"
"github.com/spf13/afero/sftp"
"github.com/pkg/sftp"
)
// SftpFs is a Fs implementation that uses functions provided by the sftp package.
//
// For details in any method, check the documentation of the sftp package
// (github.com/pkg/sftp).
type SftpFs struct{
SftpClient *sftp.Client
}
func (s SftpFs) Name() string { return "SftpFs" }
func (s SftpFs) Create(name string) (File, error) {
f, err := sftpfs.FileCreate(s.SftpClient, name)
return f, err
}
func (s SftpFs) Mkdir(name string, perm os.FileMode) error {
err := s.SftpClient.Mkdir(name)
if err != nil {
return err
}
return s.SftpClient.Chmod(name, perm)
}
func (s SftpFs) MkdirAll(path string, perm os.FileMode) error {
// Fast path: if we can tell whether path is a directory or file, stop with success or error.
dir, err := s.Stat(path)
if err == nil {
if dir.IsDir() {
return nil
}
return err
}
// Slow path: make sure parent exists and then call Mkdir for path.
i := len(path)
for i > 0 && os.IsPathSeparator(path[i-1]) { // Skip trailing path separator.
i--
}
j := i
for j > 0 && !os.IsPathSeparator(path[j-1]) { // Scan backward over element.
j--
}
if j > 1 {
// Create parent
err = s.MkdirAll(path[0:j-1], perm)
if err != nil {
return err
}
}
// Parent now exists; invoke Mkdir and use its result.
err = s.Mkdir(path, perm)
if err != nil {
// Handle arguments like "foo/." by
// double-checking that directory doesn't exist.
dir, err1 := s.Lstat(path)
if err1 == nil && dir.IsDir() {
return nil
}
return err
}
return nil
}
func (s SftpFs) Open(name string) (File, error) {
f, err := sftpfs.FileOpen(s.SftpClient, name)
return f, err
}
func (s SftpFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
return nil,nil
}
func (s SftpFs) Remove(name string) error {
return s.SftpClient.Remove(name)
}
func (s SftpFs) RemoveAll(path string) error {
// TODO have a look at os.RemoveAll
// https://github.com/golang/go/blob/master/src/os/path.go#L66
return nil
}
func (s SftpFs) Rename(oldname, newname string) error {
return s.SftpClient.Rename(oldname, newname)
}
func (s SftpFs) Stat(name string) (os.FileInfo, error) {
return s.SftpClient.Stat(name)
}
func (s SftpFs) Lstat(p string) (os.FileInfo, error) {
return s.SftpClient.Lstat(p)
}
func (s SftpFs) Chmod(name string, mode os.FileMode) error {
return s.SftpClient.Chmod(name, mode)
}
func (s SftpFs) Chtimes(name string, atime time.Time, mtime time.Time) error {
return s.SftpClient.Chtimes(name, atime, mtime)
}

View File

@@ -1,24 +0,0 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["file.go"],
importmap = "k8s.io/kubernetes/vendor/github.com/spf13/afero/sftp",
importpath = "github.com/spf13/afero/sftp",
visibility = ["//visibility:public"],
deps = ["//vendor/github.com/pkg/sftp:go_default_library"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -1,95 +0,0 @@
// Copyright © 2015 Jerry Jacobs <jerry.jacobs@xor-gate.org>.
//
// 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 sftpfs
import (
"os"
"github.com/pkg/sftp"
)
type File struct {
fd *sftp.File
}
func FileOpen(s *sftp.Client, name string) (*File, error) {
fd, err := s.Open(name)
if err != nil {
return &File{}, err
}
return &File{fd: fd}, nil
}
func FileCreate(s *sftp.Client, name string) (*File, error) {
fd, err := s.Create(name)
if err != nil {
return &File{}, err
}
return &File{fd: fd}, nil
}
func (f *File) Close() error {
return f.fd.Close()
}
func (f *File) Name() string {
return f.fd.Name()
}
func (f *File) Stat() (os.FileInfo, error) {
return f.fd.Stat()
}
func (f *File) Sync() error {
return nil
}
func (f *File) Truncate(size int64) error {
return f.fd.Truncate(size)
}
func (f *File) Read(b []byte) (n int, err error) {
return f.fd.Read(b)
}
// TODO
func (f *File) ReadAt(b []byte, off int64) (n int, err error) {
return 0,nil
}
// TODO
func (f *File) Readdir(count int) (res []os.FileInfo, err error) {
return nil,nil
}
// TODO
func (f *File) Readdirnames(n int) (names []string, err error) {
return nil,nil
}
func (f *File) Seek(offset int64, whence int) (int64, error) {
return f.fd.Seek(offset, whence)
}
func (f *File) Write(b []byte) (n int, err error) {
return f.fd.Write(b)
}
// TODO
func (f *File) WriteAt(b []byte, off int64) (n int, err error) {
return 0,nil
}
func (f *File) WriteString(s string) (ret int, err error) {
return f.fd.Write([]byte(s))
}

View File

@@ -1,286 +0,0 @@
// Copyright © 2015 Jerry Jacobs <jerry.jacobs@xor-gate.org>.
//
// 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 (
"testing"
"os"
"log"
"fmt"
"net"
"flag"
"time"
"io/ioutil"
"crypto/rsa"
_rand "crypto/rand"
"encoding/pem"
"crypto/x509"
"golang.org/x/crypto/ssh"
"github.com/pkg/sftp"
)
type SftpFsContext struct {
sshc *ssh.Client
sshcfg *ssh.ClientConfig
sftpc *sftp.Client
}
// TODO we only connect with hardcoded user+pass for now
// it should be possible to use $HOME/.ssh/id_rsa to login into the stub sftp server
func SftpConnect(user, password, host string) (*SftpFsContext, error) {
/*
pemBytes, err := ioutil.ReadFile(os.Getenv("HOME") + "/.ssh/id_rsa")
if err != nil {
return nil,err
}
signer, err := ssh.ParsePrivateKey(pemBytes)
if err != nil {
return nil,err
}
sshcfg := &ssh.ClientConfig{
User: user,
Auth: []ssh.AuthMethod{
ssh.Password(password),
ssh.PublicKeys(signer),
},
}
*/
sshcfg := &ssh.ClientConfig{
User: user,
Auth: []ssh.AuthMethod{
ssh.Password(password),
},
}
sshc, err := ssh.Dial("tcp", host, sshcfg)
if err != nil {
return nil,err
}
sftpc, err := sftp.NewClient(sshc)
if err != nil {
return nil,err
}
ctx := &SftpFsContext{
sshc: sshc,
sshcfg: sshcfg,
sftpc: sftpc,
}
return ctx,nil
}
func (ctx *SftpFsContext) Disconnect() error {
ctx.sftpc.Close()
ctx.sshc.Close()
return nil
}
// TODO for such a weird reason rootpath is "." when writing "file1" with afero sftp backend
func RunSftpServer(rootpath string) {
var (
readOnly bool
debugLevelStr string
debugLevel int
debugStderr bool
rootDir string
)
flag.BoolVar(&readOnly, "R", false, "read-only server")
flag.BoolVar(&debugStderr, "e", true, "debug to stderr")
flag.StringVar(&debugLevelStr, "l", "none", "debug level")
flag.StringVar(&rootDir, "root", rootpath, "root directory")
flag.Parse()
debugStream := ioutil.Discard
if debugStderr {
debugStream = os.Stderr
debugLevel = 1
}
// An SSH server is represented by a ServerConfig, which holds
// certificate details and handles authentication of ServerConns.
config := &ssh.ServerConfig{
PasswordCallback: func(c ssh.ConnMetadata, pass []byte) (*ssh.Permissions, error) {
// Should use constant-time compare (or better, salt+hash) in
// a production setting.
fmt.Fprintf(debugStream, "Login: %s\n", c.User())
if c.User() == "test" && string(pass) == "test" {
return nil, nil
}
return nil, fmt.Errorf("password rejected for %q", c.User())
},
}
privateBytes, err := ioutil.ReadFile("./test/id_rsa")
if err != nil {
log.Fatal("Failed to load private key", err)
}
private, err := ssh.ParsePrivateKey(privateBytes)
if err != nil {
log.Fatal("Failed to parse private key", err)
}
config.AddHostKey(private)
// Once a ServerConfig has been configured, connections can be
// accepted.
listener, err := net.Listen("tcp", "0.0.0.0:2022")
if err != nil {
log.Fatal("failed to listen for connection", err)
}
fmt.Printf("Listening on %v\n", listener.Addr())
nConn, err := listener.Accept()
if err != nil {
log.Fatal("failed to accept incoming connection", err)
}
// Before use, a handshake must be performed on the incoming
// net.Conn.
_, chans, reqs, err := ssh.NewServerConn(nConn, config)
if err != nil {
log.Fatal("failed to handshake", err)
}
fmt.Fprintf(debugStream, "SSH server established\n")
// The incoming Request channel must be serviced.
go ssh.DiscardRequests(reqs)
// Service the incoming Channel channel.
for newChannel := range chans {
// Channels have a type, depending on the application level
// protocol intended. In the case of an SFTP session, this is "subsystem"
// with a payload string of "<length=4>sftp"
fmt.Fprintf(debugStream, "Incoming channel: %s\n", newChannel.ChannelType())
if newChannel.ChannelType() != "session" {
newChannel.Reject(ssh.UnknownChannelType, "unknown channel type")
fmt.Fprintf(debugStream, "Unknown channel type: %s\n", newChannel.ChannelType())
continue
}
channel, requests, err := newChannel.Accept()
if err != nil {
log.Fatal("could not accept channel.", err)
}
fmt.Fprintf(debugStream, "Channel accepted\n")
// Sessions have out-of-band requests such as "shell",
// "pty-req" and "env". Here we handle only the
// "subsystem" request.
go func(in <-chan *ssh.Request) {
for req := range in {
fmt.Fprintf(debugStream, "Request: %v\n", req.Type)
ok := false
switch req.Type {
case "subsystem":
fmt.Fprintf(debugStream, "Subsystem: %s\n", req.Payload[4:])
if string(req.Payload[4:]) == "sftp" {
ok = true
}
}
fmt.Fprintf(debugStream, " - accepted: %v\n", ok)
req.Reply(ok, nil)
}
}(requests)
server, err := sftp.NewServer(channel, channel, debugStream, debugLevel, readOnly, rootpath)
if err != nil {
log.Fatal(err)
}
if err := server.Serve(); err != nil {
log.Fatal("sftp server completed with error:", err)
}
}
}
// MakeSSHKeyPair make a pair of public and private keys for SSH access.
// Public key is encoded in the format for inclusion in an OpenSSH authorized_keys file.
// Private Key generated is PEM encoded
func MakeSSHKeyPair(bits int, pubKeyPath, privateKeyPath string) error {
privateKey, err := rsa.GenerateKey(_rand.Reader, bits)
if err != nil {
return err
}
// generate and write private key as PEM
privateKeyFile, err := os.Create(privateKeyPath)
defer privateKeyFile.Close()
if err != nil {
return err
}
privateKeyPEM := &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(privateKey)}
if err := pem.Encode(privateKeyFile, privateKeyPEM); err != nil {
return err
}
// generate and write public key
pub, err := ssh.NewPublicKey(&privateKey.PublicKey)
if err != nil {
return err
}
return ioutil.WriteFile(pubKeyPath, ssh.MarshalAuthorizedKey(pub), 0655)
}
func TestSftpCreate(t *testing.T) {
os.Mkdir("./test", 0777)
MakeSSHKeyPair(1024, "./test/id_rsa.pub", "./test/id_rsa")
go RunSftpServer("./test/")
time.Sleep(5 * time.Second)
ctx, err := SftpConnect("test", "test", "localhost:2022")
if err != nil {
t.Fatal(err)
}
defer ctx.Disconnect()
var AppFs Fs = SftpFs{
SftpClient: ctx.sftpc,
}
AppFs.MkdirAll("test/dir1/dir2/dir3", os.FileMode(0777))
AppFs.Mkdir("test/foo", os.FileMode(0000))
AppFs.Chmod("test/foo", os.FileMode(0700))
AppFs.Mkdir("test/bar", os.FileMode(0777))
file, err := AppFs.Create("file1")
if err != nil {
t.Error(err)
}
defer file.Close()
file.Write([]byte("hello\t"))
file.WriteString("world!\n")
f1, err := AppFs.Open("file1")
if err != nil {
log.Fatalf("open: %v", err)
}
defer f1.Close()
b := make([]byte, 100)
_, err = f1.Read(b)
fmt.Println(string(b))
// TODO check here if "hello\tworld\n" is in buffer b
}

View File

@@ -21,32 +21,33 @@ import (
// 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
off int
files []os.FileInfo
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.Base != nil {
f.Base.Close()
}
if f.layer != nil {
return f.layer.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 {
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 {
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
@@ -54,109 +55,154 @@ func (f *UnionFile) Read(s []byte) (int, error) {
}
return n, err
}
if f.base != nil {
return f.base.Read(s)
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)
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)
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)
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)
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)
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)
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)
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)
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()
if f.Layer != nil {
return f.Layer.Name()
}
return f.base.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
// 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 files = make(map[string]os.FileInfo)
var rfi []os.FileInfo
if f.layer != nil {
rfi, err = f.layer.Readdir(-1)
var lfi []os.FileInfo
if f.Layer != nil {
lfi, err = f.Layer.Readdir(-1)
if err != nil {
return nil, err
}
for _, fi := range rfi {
files[fi.Name()] = fi
}
}
if f.base != nil {
rfi, err = f.base.Readdir(-1)
var bfi []os.FileInfo
if f.Base != nil {
bfi, err = f.Base.Readdir(-1)
if err != nil {
return nil, err
}
for _, fi := range rfi {
if _, exists := files[fi.Name()]; !exists {
files[fi.Name()] = fi
}
}
}
for _, fi := range files {
f.files = append(f.files, fi)
merged, err := merge(lfi, bfi)
if err != nil {
return nil, err
}
f.files = append(f.files, merged...)
}
if c == -1 {
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
}
@@ -174,53 +220,53 @@ func (f *UnionFile) Readdirnames(c int) ([]string, error) {
}
func (f *UnionFile) Stat() (os.FileInfo, error) {
if f.layer != nil {
return f.layer.Stat()
if f.Layer != nil {
return f.Layer.Stat()
}
if f.base != nil {
return f.base.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()
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()
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)
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)
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)
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)
if f.Base != nil {
return f.Base.WriteString(s)
}
return 0, BADFD
}

View File

@@ -20,7 +20,6 @@ import (
"bytes"
"fmt"
"io"
"log"
"os"
"path/filepath"
"strings"
@@ -46,7 +45,7 @@ func WriteReader(fs Fs, path string, r io.Reader) (err error) {
err = fs.MkdirAll(ospath, 0777) // rwx, rw, r
if err != nil {
if err != os.ErrExist {
log.Panicln(err)
return err
}
}
}
@@ -157,7 +156,7 @@ func UnicodeSanitize(s string) string {
return string(target)
}
// Transform characters with accents into plan forms
// 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))
@@ -295,10 +294,10 @@ func IsEmpty(fs Fs, path string) (bool, error) {
}
if fi.IsDir() {
f, err := fs.Open(path)
defer f.Close()
if err != nil {
return false, err
}
defer f.Close()
list, err := f.Readdir(-1)
return len(list) == 0, nil
}