diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 00000000..011f9e5d --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,37 @@ +on: + push: + branches: [ main ] + pull_request: +name: Test +jobs: + test: + strategy: + matrix: + go-version: [1.18.x,1.19.x,1.20.x] + platform: [ubuntu-latest, macos-latest, windows-latest] + runs-on: ${{ matrix.platform }} + steps: + - name: Install Go + uses: actions/setup-go@v3 + with: + go-version: ${{ matrix.go-version }} + - name: Install staticcheck + if: matrix.go-version == '1.20.x' + run: go install honnef.co/go/tools/cmd/staticcheck@latest + shell: bash + - name: Update PATH + run: echo "$(go env GOPATH)/bin" >> $GITHUB_PATH + shell: bash + - name: Checkout code + uses: actions/checkout@v1 + - name: Fmt + if: matrix.platform != 'windows-latest' # :( + run: "diff <(gofmt -d .) <(printf '')" + shell: bash + - name: Vet + run: go vet ./... + - name: Staticcheck + if: matrix.go-version == '1.20.x' + run: staticcheck ./... + - name: Test + run: go test -race ./... \ No newline at end of file diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index e944f594..00000000 --- a/.travis.yml +++ /dev/null @@ -1,26 +0,0 @@ -sudo: false -language: go -arch: - - amd64 - - ppc64e - -go: - - "1.14" - - "1.15" - - "1.16" - - tip - -os: - - linux - - osx - -matrix: - allow_failures: - - go: tip - fast_finish: true - -script: - - go build -v ./... - - go test -count=1 -cover -race -v ./... - - go vet ./... - - FILES=$(gofmt -s -l . zipfs sftpfs mem tarfs); if [[ -n "${FILES}" ]]; then echo "You have go format errors; gofmt your changes"; exit 1; fi diff --git a/README.md b/README.md index cab257f5..3bafbfdf 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ A FileSystem Abstraction System for Go -[![Build Status](https://travis-ci.org/spf13/afero.svg)](https://travis-ci.org/spf13/afero) [![Build status](https://ci.appveyor.com/api/projects/status/github/spf13/afero?branch=master&svg=true)](https://ci.appveyor.com/project/spf13/afero) [![GoDoc](https://godoc.org/github.com/spf13/afero?status.svg)](https://godoc.org/github.com/spf13/afero) [![Join the chat at https://gitter.im/spf13/afero](https://badges.gitter.im/Dev%20Chat.svg)](https://gitter.im/spf13/afero?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +[![Test](https://github.com/spf13/afero/actions/workflows/test.yml/badge.svg)](https://github.com/spf13/afero/actions/workflows/test.yml) [![GoDoc](https://godoc.org/github.com/spf13/afero?status.svg)](https://godoc.org/github.com/spf13/afero) [![Join the chat at https://gitter.im/spf13/afero](https://badges.gitter.im/Dev%20Chat.svg)](https://gitter.im/spf13/afero?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) # Overview diff --git a/afero.go b/afero.go index 469ff7d2..39f65852 100644 --- a/afero.go +++ b/afero.go @@ -97,14 +97,14 @@ type Fs interface { // Chown changes the uid and gid of the named file. Chown(name string, uid, gid int) error - //Chtimes changes the access and modification times of the named file + // 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") + ErrOutOfRange = errors.New("out of range") + ErrTooLarge = errors.New("too large") ErrFileNotFound = os.ErrNotExist ErrFileExists = os.ErrExist ErrDestinationExists = os.ErrExist diff --git a/afero_test.go b/afero_test.go index e2c13624..9a1d84f2 100644 --- a/afero_test.go +++ b/afero_test.go @@ -18,6 +18,7 @@ import ( "bytes" "fmt" "io" + iofs "io/fs" "io/ioutil" "os" "path/filepath" @@ -27,8 +28,10 @@ import ( "testing" ) -var testName = "test.txt" -var Fss = []Fs{&MemMapFs{}, &OsFs{}} +var ( + testName = "test.txt" + Fss = []Fs{&MemMapFs{}, &OsFs{}} +) var testRegistry map[Fs][]string = make(map[Fs][]string) @@ -44,7 +47,6 @@ func testDir(fs Fs) string { func tmpFile(fs Fs) File { x, err := TempFile(fs, "", "afero") - if err != nil { panic(fmt.Sprint("unable to work with temp file", err)) } @@ -54,7 +56,7 @@ func tmpFile(fs Fs) File { return x } -//Read with length 0 should not return EOF. +// Read with length 0 should not return EOF. func TestRead0(t *testing.T) { for _, fs := range Fss { f := tmpFile(fs) @@ -82,7 +84,7 @@ func TestOpenFile(t *testing.T) { tmp := testDir(fs) path := filepath.Join(tmp, testName) - f, err := fs.OpenFile(path, os.O_RDWR|os.O_CREATE, 0600) + f, err := fs.OpenFile(path, os.O_RDWR|os.O_CREATE, 0o600) if err != nil { t.Error(fs.Name(), "OpenFile (O_CREATE) failed:", err) continue @@ -90,7 +92,7 @@ func TestOpenFile(t *testing.T) { io.WriteString(f, "initial") f.Close() - f, err = fs.OpenFile(path, os.O_WRONLY|os.O_APPEND, 0600) + f, err = fs.OpenFile(path, os.O_WRONLY|os.O_APPEND, 0o600) if err != nil { t.Error(fs.Name(), "OpenFile (O_APPEND) failed:", err) continue @@ -98,7 +100,7 @@ func TestOpenFile(t *testing.T) { io.WriteString(f, "|append") f.Close() - f, err = fs.OpenFile(path, os.O_RDONLY, 0600) + f, _ = fs.OpenFile(path, os.O_RDONLY, 0o600) contents, _ := ioutil.ReadAll(f) expectedContents := "initial|append" if string(contents) != expectedContents { @@ -106,7 +108,7 @@ func TestOpenFile(t *testing.T) { } f.Close() - f, err = fs.OpenFile(path, os.O_RDWR|os.O_TRUNC, 0600) + f, err = fs.OpenFile(path, os.O_RDWR|os.O_TRUNC, 0o600) if err != nil { t.Error(fs.Name(), "OpenFile (O_TRUNC) failed:", err) continue @@ -332,7 +334,7 @@ func TestSeek(t *testing.T) { whence int out int64 } - var tests = []test{ + tests := []test{ {0, 1, int64(len(data))}, {0, 0, 0}, {5, 0, 5}, @@ -423,7 +425,7 @@ func setupTestDirReusePath(t *testing.T, fs Fs, path string) string { func setupTestFiles(t *testing.T, fs Fs, path string) string { testSubDir := filepath.Join(path, "more", "subdirectories", "for", "testing", "we") - err := fs.MkdirAll(testSubDir, 0700) + err := fs.MkdirAll(testSubDir, 0o700) if err != nil && !os.IsExist(err) { t.Fatal(err) } @@ -530,22 +532,43 @@ func TestReaddirSimple(t *testing.T) { func TestReaddir(t *testing.T) { defer removeAllTestFiles(t) - for num := 0; num < 6; num++ { + const nums = 6 + for num := 0; num < nums; num++ { outputs := make([]string, len(Fss)) infos := make([]string, len(Fss)) for i, fs := range Fss { testSubDir := setupTestDir(t, fs) - //tDir := filepath.Dir(testSubDir) root, err := fs.Open(testSubDir) if err != nil { t.Fatal(err) } - defer root.Close() - for j := 0; j < 6; j++ { + infosn := make([]string, nums) + + for j := 0; j < nums; j++ { info, err := root.Readdir(num) outputs[i] += fmt.Sprintf("%v Error: %v\n", myFileInfo(info), err) - infos[i] += fmt.Sprintln(len(info), err) + s := fmt.Sprintln(len(info), err) + infosn[j] = s + infos[i] += s + } + root.Close() + + // Also check fs.ReadDirFile interface if implemented + if _, ok := root.(iofs.ReadDirFile); ok { + root, err = fs.Open(testSubDir) + if err != nil { + t.Fatal(err) + } + defer root.Close() + + for j := 0; j < nums; j++ { + dirEntries, err := root.(iofs.ReadDirFile).ReadDir(num) + s := fmt.Sprintln(len(dirEntries), err) + if s != infosn[j] { + t.Fatalf("%s: %s != %s", fs.Name(), s, infosn[j]) + } + } } } @@ -615,7 +638,7 @@ func TestReaddirAll(t *testing.T) { if err != nil { t.Fatal(err) } - var namesRoot = []string{} + namesRoot := []string{} for _, e := range rootInfo { namesRoot = append(namesRoot, e.Name()) } @@ -630,7 +653,7 @@ func TestReaddirAll(t *testing.T) { if err != nil { t.Fatal(err) } - var namesSub = []string{} + namesSub := []string{} for _, e := range subInfo { namesSub = append(namesSub, e.Name()) } @@ -700,7 +723,7 @@ func removeAllTestFiles(t *testing.T) { func equal(name1, name2 string) (r bool) { switch runtime.GOOS { case "windows": - r = strings.ToLower(name1) == strings.ToLower(name2) + r = strings.EqualFold(name1, name2) default: r = name1 == name2 } diff --git a/appveyor.yml b/appveyor.yml index 5d2f34bf..65e20e8c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,3 +1,5 @@ +# This currently does nothing. We have moved to GitHub action, but this is kept +# until spf13 has disabled this project in AppVeyor. version: '{build}' clone_folder: C:\gopath\src\github.com\spf13\afero environment: @@ -6,10 +8,3 @@ build_script: - cmd: >- go version - go env - - go get -v github.com/spf13/afero/... - - go build -v github.com/spf13/afero/... -test_script: -- cmd: go test -count=1 -cover -race -v github.com/spf13/afero/... diff --git a/basepath.go b/basepath.go index 4f983282..2e72793a 100644 --- a/basepath.go +++ b/basepath.go @@ -1,6 +1,7 @@ package afero import ( + "io/fs" "os" "path/filepath" "runtime" @@ -8,7 +9,10 @@ import ( "time" ) -var _ Lstater = (*BasePathFs)(nil) +var ( + _ Lstater = (*BasePathFs)(nil) + _ fs.ReadDirFile = (*BasePathFile)(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 @@ -33,6 +37,13 @@ func (f *BasePathFile) Name() string { return strings.TrimPrefix(sourcename, filepath.Clean(f.path)) } +func (f *BasePathFile) ReadDir(n int) ([]fs.DirEntry, error) { + if rdf, ok := f.File.(fs.ReadDirFile); ok { + return rdf.ReadDir(n) + } + return readDirFile{f.File}.ReadDir(n) +} + func NewBasePathFs(source Fs, path string) Fs { return &BasePathFs{source: source, path: path} } diff --git a/basepath_test.go b/basepath_test.go index e314c05f..1c46abd2 100644 --- a/basepath_test.go +++ b/basepath_test.go @@ -9,7 +9,7 @@ import ( func TestBasePath(t *testing.T) { baseFs := &MemMapFs{} - baseFs.MkdirAll("/base/path/tmp", 0777) + baseFs.MkdirAll("/base/path/tmp", 0o777) bp := NewBasePathFs(baseFs, "/base/path") if _, err := bp.Create("/tmp/foo"); err != nil { @@ -23,8 +23,8 @@ func TestBasePath(t *testing.T) { func TestBasePathRoot(t *testing.T) { baseFs := &MemMapFs{} - baseFs.MkdirAll("/base/path/foo/baz", 0777) - baseFs.MkdirAll("/base/path/boo/", 0777) + baseFs.MkdirAll("/base/path/foo/baz", 0o777) + baseFs.MkdirAll("/base/path/boo/", 0o777) bp := NewBasePathFs(baseFs, "/base/path") rd, err := ReadDir(bp, string(os.PathSeparator)) @@ -56,7 +56,6 @@ func TestRealPath(t *testing.T) { subDir := filepath.Join(baseDir, "s1") realPath, err := bp.RealPath("/s1") - if err != nil { t.Errorf("Got error %s", err) } @@ -77,7 +76,6 @@ func TestRealPath(t *testing.T) { // is not inside the base file system. // The user will receive an os.ErrNotExist later. surrealPath, err := bp.RealPath(anotherDir) - if err != nil { t.Errorf("Got error %s", err) } @@ -88,7 +86,6 @@ func TestRealPath(t *testing.T) { t.Errorf("Expected \n%s got \n%s", excpected, surrealPath) } } - } func TestNestedBasePaths(t *testing.T) { @@ -119,7 +116,7 @@ func TestNestedBasePaths(t *testing.T) { } for _, s := range specs { - if err := s.BaseFs.MkdirAll(s.FileName, 0755); err != nil { + if err := s.BaseFs.MkdirAll(s.FileName, 0o755); err != nil { t.Errorf("Got error %s", err.Error()) } if _, err := s.BaseFs.Stat(s.FileName); err != nil { @@ -143,9 +140,9 @@ func TestNestedBasePaths(t *testing.T) { func TestBasePathOpenFile(t *testing.T) { baseFs := &MemMapFs{} - baseFs.MkdirAll("/base/path/tmp", 0777) + baseFs.MkdirAll("/base/path/tmp", 0o777) bp := NewBasePathFs(baseFs, "/base/path") - f, err := bp.OpenFile("/tmp/file.txt", os.O_CREATE, 0600) + f, err := bp.OpenFile("/tmp/file.txt", os.O_CREATE, 0o600) if err != nil { t.Fatalf("failed to open file: %v", err) } @@ -156,7 +153,7 @@ func TestBasePathOpenFile(t *testing.T) { func TestBasePathCreate(t *testing.T) { baseFs := &MemMapFs{} - baseFs.MkdirAll("/base/path/tmp", 0777) + baseFs.MkdirAll("/base/path/tmp", 0o777) bp := NewBasePathFs(baseFs, "/base/path") f, err := bp.Create("/tmp/file.txt") if err != nil { @@ -169,7 +166,7 @@ func TestBasePathCreate(t *testing.T) { func TestBasePathTempFile(t *testing.T) { baseFs := &MemMapFs{} - baseFs.MkdirAll("/base/path/tmp", 0777) + baseFs.MkdirAll("/base/path/tmp", 0o777) bp := NewBasePathFs(baseFs, "/base/path") tDir, err := TempDir(bp, "/tmp", "") diff --git a/composite_test.go b/composite_test.go index 9cfbed92..88fd49b9 100644 --- a/composite_test.go +++ b/composite_test.go @@ -54,12 +54,12 @@ func TestUnionCreateExisting(t *testing.T) { roBase := &ReadOnlyFs{source: base} ufs := NewCopyOnWriteFs(roBase, &MemMapFs{}) - base.MkdirAll("/home/test", 0777) + base.MkdirAll("/home/test", 0o777) fh, _ := base.Create("/home/test/file.txt") fh.WriteString("This is a test") fh.Close() - fh, err := ufs.OpenFile("/home/test/file.txt", os.O_RDWR, 0666) + fh, err := ufs.OpenFile("/home/test/file.txt", os.O_RDWR, 0o666) if err != nil { t.Errorf("Failed to open file r/w: %s", err) } @@ -79,7 +79,7 @@ func TestUnionCreateExisting(t *testing.T) { fh.Close() fh, _ = base.Open("/home/test/file.txt") - data, err = ioutil.ReadAll(fh) + data, _ = ioutil.ReadAll(fh) if string(data) != "This is a test" { t.Errorf("Got wrong data in base file") } @@ -95,7 +95,6 @@ func TestUnionCreateExisting(t *testing.T) { default: t.Errorf("Create failed on existing file") } - } func TestUnionMergeReaddir(t *testing.T) { @@ -104,7 +103,7 @@ func TestUnionMergeReaddir(t *testing.T) { ufs := &CopyOnWriteFs{base: roBase, layer: &MemMapFs{}} - base.MkdirAll("/home/test", 0777) + base.MkdirAll("/home/test", 0o777) fh, _ := base.Create("/home/test/file.txt") fh.WriteString("This is a test") fh.Close() @@ -130,12 +129,12 @@ func TestExistingDirectoryCollisionReaddir(t *testing.T) { ufs := &CopyOnWriteFs{base: roBase, layer: overlay} - base.MkdirAll("/home/test", 0777) + base.MkdirAll("/home/test", 0o777) fh, _ := base.Create("/home/test/file.txt") fh.WriteString("This is a test") fh.Close() - overlay.MkdirAll("home/test", 0777) + overlay.MkdirAll("home/test", 0o777) fh, _ = overlay.Create("/home/test/file2.txt") fh.WriteString("This is a test") fh.Close() @@ -170,7 +169,7 @@ func TestNestedDirBaseReaddir(t *testing.T) { ufs := &CopyOnWriteFs{base: roBase, layer: overlay} - base.MkdirAll("/home/test/foo/bar", 0777) + base.MkdirAll("/home/test/foo/bar", 0o777) fh, _ := base.Create("/home/test/file.txt") fh.WriteString("This is a test") fh.Close() @@ -182,7 +181,7 @@ func TestNestedDirBaseReaddir(t *testing.T) { fh.WriteString("This is a test") fh.Close() - overlay.MkdirAll("/", 0777) + overlay.MkdirAll("/", 0o777) // Opening something only in the base fh, _ = ufs.Open("/home/test/foo") @@ -205,8 +204,8 @@ func TestNestedDirOverlayReaddir(t *testing.T) { ufs := &CopyOnWriteFs{base: roBase, layer: overlay} - base.MkdirAll("/", 0777) - overlay.MkdirAll("/home/test/foo/bar", 0777) + base.MkdirAll("/", 0o777) + overlay.MkdirAll("/home/test/foo/bar", 0o777) fh, _ := overlay.Create("/home/test/file.txt") fh.WriteString("This is a test") fh.Close() @@ -239,8 +238,8 @@ func TestNestedDirOverlayOsFsReaddir(t *testing.T) { ufs := &CopyOnWriteFs{base: roBase, layer: overlay} - base.MkdirAll("/", 0777) - overlay.MkdirAll("/home/test/foo/bar", 0777) + base.MkdirAll("/", 0o777) + overlay.MkdirAll("/home/test/foo/bar", 0o777) fh, _ := overlay.Create("/home/test/file.txt") fh.WriteString("This is a test") fh.Close() @@ -274,12 +273,12 @@ func TestCopyOnWriteFsWithOsFs(t *testing.T) { ufs := &CopyOnWriteFs{base: roBase, layer: overlay} - base.MkdirAll("/home/test", 0777) + base.MkdirAll("/home/test", 0o777) fh, _ := base.Create("/home/test/file.txt") fh.WriteString("This is a test") fh.Close() - overlay.MkdirAll("home/test", 0777) + overlay.MkdirAll("home/test", 0o777) fh, _ = overlay.Create("/home/test/file2.txt") fh.WriteString("This is a test") fh.Close() @@ -315,7 +314,7 @@ func TestUnionCacheWrite(t *testing.T) { ufs := NewCacheOnReadFs(base, layer, 0) - base.Mkdir("/data", 0777) + base.Mkdir("/data", 0o777) fh, err := ufs.Create("/data/file.txt") if err != nil { @@ -326,9 +325,9 @@ func TestUnionCacheWrite(t *testing.T) { t.Errorf("Failed to write file") } - fh.Seek(0, os.SEEK_SET) + fh.Seek(0, io.SeekStart) buf := make([]byte, 4) - _, err = fh.Read(buf) + _, _ = fh.Read(buf) fh.Write([]byte(" IS A")) fh.Close() @@ -344,7 +343,7 @@ func TestUnionCacheExpire(t *testing.T) { layer := &MemMapFs{} ufs := &CacheOnReadFs{base: base, layer: layer, cacheTime: 1 * time.Second} - base.Mkdir("/data", 0777) + base.Mkdir("/data", 0o777) fh, err := ufs.Create("/data/file.txt") if err != nil { @@ -449,7 +448,7 @@ func TestUnionFileReaddirDuplicateEmpty(t *testing.T) { // Overlay shares same empty directory as base overlay := NewMemMapFs() - err = overlay.Mkdir(dir, 0700) + err = overlay.Mkdir(dir, 0o700) if err != nil { t.Fatal(err) } @@ -479,7 +478,7 @@ func TestUnionFileReaddirAskForTooMany(t *testing.T) { const testFiles = 5 for i := 0; i < testFiles; i++ { - WriteFile(base, fmt.Sprintf("file%d.txt", i), []byte("afero"), 0777) + WriteFile(base, fmt.Sprintf("file%d.txt", i), []byte("afero"), 0o777) } ufs := &CopyOnWriteFs{base: base, layer: overlay} diff --git a/const_bsds.go b/const_bsds.go index 18b45824..30855de5 100644 --- a/const_bsds.go +++ b/const_bsds.go @@ -11,7 +11,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -// +build aix darwin openbsd freebsd netbsd dragonfly +//go:build aix || darwin || openbsd || freebsd || netbsd || dragonfly || zos +// +build aix darwin openbsd freebsd netbsd dragonfly zos package afero diff --git a/const_win_unix.go b/const_win_unix.go index 2b850e4d..12792d21 100644 --- a/const_win_unix.go +++ b/const_win_unix.go @@ -10,12 +10,8 @@ // 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 -// +build !aix +//go:build !darwin && !openbsd && !freebsd && !dragonfly && !netbsd && !aix && !zos +// +build !darwin,!openbsd,!freebsd,!dragonfly,!netbsd,!aix,!zos package afero diff --git a/copyOnWriteFs.go b/copyOnWriteFs.go index 6ff8f309..184d6dd7 100644 --- a/copyOnWriteFs.go +++ b/copyOnWriteFs.go @@ -223,7 +223,7 @@ func (u *CopyOnWriteFs) OpenFile(name string, flag int, perm os.FileMode) (File, return nil, err } if isaDir { - if err = u.layer.MkdirAll(dir, 0777); err != nil { + if err = u.layer.MkdirAll(dir, 0o777); err != nil { return nil, err } return u.layer.OpenFile(name, flag, perm) @@ -247,8 +247,9 @@ func (u *CopyOnWriteFs) OpenFile(name string, flag int, perm os.FileMode) (File, // 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 +// +// 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) @@ -322,5 +323,5 @@ func (u *CopyOnWriteFs) MkdirAll(name string, perm os.FileMode) error { } func (u *CopyOnWriteFs) Create(name string) (File, error) { - return u.OpenFile(name, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0666) + return u.OpenFile(name, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0o666) } diff --git a/copyOnWriteFs_test.go b/copyOnWriteFs_test.go index 94ba7255..c94ff1ea 100644 --- a/copyOnWriteFs_test.go +++ b/copyOnWriteFs_test.go @@ -16,9 +16,9 @@ func TestCopyOnWrite(t *testing.T) { compositeFs := NewCopyOnWriteFs(NewReadOnlyFs(NewOsFs()), osFs) - var dir = filepath.Join(writeDir, "some/path") + dir := filepath.Join(writeDir, "some/path") - err = compositeFs.MkdirAll(dir, 0744) + err = compositeFs.MkdirAll(dir, 0o744) if err != nil { t.Fatal(err) } @@ -31,13 +31,13 @@ func TestCopyOnWrite(t *testing.T) { // We want the composite file system to behave like the OS file system // on Mkdir and MkdirAll for _, fs := range []Fs{osFs, compositeFs} { - err = fs.Mkdir(dir, 0744) + err = fs.Mkdir(dir, 0o744) if err == nil || !os.IsExist(err) { t.Errorf("Mkdir: Got %q for %T", err, fs) } // MkdirAll does not return an error when the directory already exists - err = fs.MkdirAll(dir, 0744) + err = fs.MkdirAll(dir, 0o744) if err != nil { t.Errorf("MkdirAll: Got %q for %T", err, fs) } @@ -49,7 +49,7 @@ func TestCopyOnWriteFileInMemMapBase(t *testing.T) { base := &MemMapFs{} layer := &MemMapFs{} - if err := WriteFile(base, "base.txt", []byte("base"), 0755); err != nil { + if err := WriteFile(base, "base.txt", []byte("base"), 0o755); err != nil { t.Fatalf("Failed to write file: %s", err) } diff --git a/gcsfs/file.go b/gcsfs/file.go index f916bd22..671c0466 100644 --- a/gcsfs/file.go +++ b/gcsfs/file.go @@ -36,7 +36,7 @@ import ( // GcsFs is the Afero version adapted for GCS type GcsFile struct { openFlags int - fhOffset int64 //File handle specific offset + fhOffset int64 // File handle specific offset closed bool ReadDirIt stiface.ObjectIterator resource *gcsFileResource @@ -105,13 +105,13 @@ func (o *GcsFile) Seek(newOffset int64, whence int) (int64, error) { return 0, ErrFileClosed } - //Since this is an expensive operation; let's make sure we need it + // Since this is an expensive operation; let's make sure we need it if (whence == 0 && newOffset == o.fhOffset) || (whence == 1 && newOffset == 0) { return o.fhOffset, nil } log.Printf("WARNING: Seek behavior triggered, highly inefficent. Offset before seek is at %d\n", o.fhOffset) - //Fore the reader/writers to be reopened (at correct offset) + // Fore the reader/writers to be reopened (at correct offset) err := o.Sync() if err != nil { return 0, err @@ -197,7 +197,7 @@ func (o *GcsFile) readdirImpl(count int) ([]*FileInfo, error) { path := o.resource.fs.ensureTrailingSeparator(o.resource.name) if o.ReadDirIt == nil { - //log.Printf("Querying path : %s\n", path) + // log.Printf("Querying path : %s\n", path) bucketName, bucketPath := o.resource.fs.splitName(path) o.ReadDirIt = o.resource.fs.client.Bucket(bucketName).Objects( @@ -246,7 +246,7 @@ func (o *GcsFile) readdirImpl(count int) ([]*FileInfo, error) { // break //} } - //return res, nil + // return res, nil } func (o *GcsFile) Readdir(count int) ([]os.FileInfo, error) { diff --git a/gcsfs/file_info.go b/gcsfs/file_info.go index 0a42d38f..92e30460 100644 --- a/gcsfs/file_info.go +++ b/gcsfs/file_info.go @@ -110,6 +110,7 @@ func (fi *FileInfo) Name() string { func (fi *FileInfo) Size() int64 { return fi.size } + func (fi *FileInfo) Mode() os.FileMode { if fi.IsDir() { return os.ModeDir | fi.fileMode diff --git a/gcsfs/file_resource.go b/gcsfs/file_resource.go index c25ad51d..06d5c544 100644 --- a/gcsfs/file_resource.go +++ b/gcsfs/file_resource.go @@ -146,7 +146,7 @@ func (o *gcsFileResource) ReadAt(p []byte, off int64) (n int, err error) { return 0, err } - //Then read at the correct offset. + // Then read at the correct offset. r, err := o.obj.NewRangeReader(o.ctx, off, -1) if err != nil { return 0, err @@ -160,7 +160,7 @@ func (o *gcsFileResource) ReadAt(p []byte, off int64) (n int, err error) { } func (o *gcsFileResource) WriteAt(b []byte, off int64) (n int, err error) { - //If the writer is opened and at the correct offset we're good! + // If the writer is opened and at the correct offset we're good! if off == o.offset && o.writer != nil { n, err = o.writer.Write(b) o.offset += int64(n) @@ -251,7 +251,7 @@ func (o *gcsFileResource) Truncate(wantedSize int64) error { } for written < wantedSize { - //Bulk up padding writes + // Bulk up padding writes paddingBytes := bytes.Repeat([]byte(" "), min(maxWriteSize, int(wantedSize-written))) n := 0 diff --git a/gcsfs/fs.go b/gcsfs/fs.go index 51db9958..b2a78fcc 100644 --- a/gcsfs/fs.go +++ b/gcsfs/fs.go @@ -29,7 +29,7 @@ import ( ) const ( - defaultFileMode = 0755 + defaultFileMode = 0o755 gsPrefix = "gs://" ) @@ -42,7 +42,7 @@ type Fs struct { buckets map[string]stiface.BucketHandle rawGcsObjects map[string]*GcsFile - autoRemoveEmptyFolders bool //trigger for creating "virtual folders" (not required by GCSs) + autoRemoveEmptyFolders bool // trigger for creating "virtual folders" (not required by GCSs) } func NewGcsFs(ctx context.Context, client stiface.Client) *Fs { @@ -71,6 +71,7 @@ func (fs *Fs) ensureTrailingSeparator(s string) string { } return s } + func (fs *Fs) ensureNoLeadingSeparator(s string) string { if len(s) > 0 && strings.HasPrefix(s, fs.separator) { s = s[len(fs.separator):] @@ -200,7 +201,7 @@ func (fs *Fs) MkdirAll(path string, perm os.FileMode) error { if f == "" && i != 0 { continue // it's the last item - it should be empty } - //Don't force a delimiter prefix + // Don't force a delimiter prefix if root != "" { root = root + fs.separator + f } else { @@ -302,6 +303,9 @@ func (fs *Fs) Remove(name string) error { } var infos []os.FileInfo infos, err = dir.Readdir(0) + if err != nil { + return err + } if len(infos) > 0 { return syscall.ENOTEMPTY } @@ -345,6 +349,9 @@ func (fs *Fs) RemoveAll(path string) error { var infos []os.FileInfo infos, err = dir.Readdir(0) + if err != nil { + return err + } for _, info := range infos { nameToRemove := fs.normSeparators(info.Name()) err = fs.RemoveAll(path + fs.separator + nameToRemove) diff --git a/gcsfs/gcs.go b/gcsfs/gcs.go index 78cc924f..c94b1421 100644 --- a/gcsfs/gcs.go +++ b/gcsfs/gcs.go @@ -76,39 +76,51 @@ func NewGcsFSFromClientWithSeparator(ctx context.Context, client *storage.Client func (fs *GcsFs) Name() string { return fs.source.Name() } + func (fs *GcsFs) Create(name string) (afero.File, error) { return fs.source.Create(name) } + func (fs *GcsFs) Mkdir(name string, perm os.FileMode) error { return fs.source.Mkdir(name, perm) } + func (fs *GcsFs) MkdirAll(path string, perm os.FileMode) error { return fs.source.MkdirAll(path, perm) } + func (fs *GcsFs) Open(name string) (afero.File, error) { return fs.source.Open(name) } + func (fs *GcsFs) OpenFile(name string, flag int, perm os.FileMode) (afero.File, error) { return fs.source.OpenFile(name, flag, perm) } + func (fs *GcsFs) Remove(name string) error { return fs.source.Remove(name) } + func (fs *GcsFs) RemoveAll(path string) error { return fs.source.RemoveAll(path) } + func (fs *GcsFs) Rename(oldname, newname string) error { return fs.source.Rename(oldname, newname) } + func (fs *GcsFs) Stat(name string) (os.FileInfo, error) { return fs.source.Stat(name) } + func (fs *GcsFs) Chmod(name string, mode os.FileMode) error { return fs.source.Chmod(name, mode) } + func (fs *GcsFs) Chtimes(name string, atime time.Time, mtime time.Time) error { return fs.source.Chtimes(name, atime, mtime) } + func (fs *GcsFs) Chown(name string, uid, gid int) error { return fs.source.Chown(name, uid, gid) } diff --git a/gcsfs/gcs_mocks.go b/gcsfs/gcs_mocks.go index 272eb9f1..b71b2924 100644 --- a/gcsfs/gcs_mocks.go +++ b/gcsfs/gcs_mocks.go @@ -166,7 +166,7 @@ func (w *writerMock) Close() error { if w.file == nil { var err error if strings.HasSuffix(w.name, "/") { - err = w.fs.Mkdir(w.name, 0755) + err = w.fs.Mkdir(w.name, 0o755) if err != nil { return err } diff --git a/gcsfs/gcs_test.go b/gcsfs/gcs_test.go index 0809c2ba..67794be5 100644 --- a/gcsfs/gcs_test.go +++ b/gcsfs/gcs_test.go @@ -122,7 +122,7 @@ func TestMain(m *testing.M) { gcsAfs = &afero.Afero{Fs: &GcsFs{NewGcsFs(ctx, mockClient)}} // Uncomment to use the real, not mocked, client - //gcsAfs = &Afero{Fs: &GcsFs{gcsfs.NewGcsFs(ctx, client)}} + // gcsAfs = &Afero{Fs: &GcsFs{gcsfs.NewGcsFs(ctx, client)}} exitCode = m.Run() } @@ -341,7 +341,7 @@ func TestGcsSeek(t *testing.T) { t.Fatalf("opening %v: %v", name, err) } - var tests = []struct { + tests := []struct { offIn int64 whence int offOut int64 @@ -477,7 +477,7 @@ func TestGcsOpenFile(t *testing.T) { } for _, name := range names { - file, err := gcsAfs.OpenFile(name, os.O_RDONLY, 0400) + file, err := gcsAfs.OpenFile(name, os.O_RDONLY, 0o400) if !f.exists { if (f.name != "" && !errors.Is(err, syscall.ENOENT)) || (f.name == "" && !errors.Is(err, ErrNoBucketInName)) { @@ -496,7 +496,7 @@ func TestGcsOpenFile(t *testing.T) { t.Fatalf("failed to close a file \"%s\": %s", name, err) } - file, err = gcsAfs.OpenFile(name, os.O_CREATE, 0600) + _, err = gcsAfs.OpenFile(name, os.O_CREATE, 0o600) if !errors.Is(err, syscall.EPERM) { t.Errorf("%v: open for write: got %v, expected %v", name, err, syscall.EPERM) } @@ -714,7 +714,7 @@ func TestGcsMkdir(t *testing.T) { t.Run("empty", func(t *testing.T) { emptyDirName := bucketName - err := gcsAfs.Mkdir(emptyDirName, 0755) + err := gcsAfs.Mkdir(emptyDirName, 0o755) if err == nil { t.Fatal("did not fail upon creation of an empty folder") } @@ -723,7 +723,7 @@ func TestGcsMkdir(t *testing.T) { dirName := filepath.Join(bucketName, "a-test-dir") var err error - err = gcsAfs.Mkdir(dirName, 0755) + err = gcsAfs.Mkdir(dirName, 0o755) if err != nil { t.Fatal("failed to create a folder with error", err) } @@ -739,7 +739,7 @@ func TestGcsMkdir(t *testing.T) { t.Errorf("%s: mode is not directory", dirName) } - if info.Mode() != os.ModeDir|0755 { + if info.Mode() != os.ModeDir|0o755 { t.Errorf("%s: wrong permissions, expected drwxr-xr-x, got %s", dirName, info.Mode()) } @@ -754,7 +754,7 @@ func TestGcsMkdirAll(t *testing.T) { t.Run("empty", func(t *testing.T) { emptyDirName := bucketName - err := gcsAfs.MkdirAll(emptyDirName, 0755) + err := gcsAfs.MkdirAll(emptyDirName, 0o755) if err == nil { t.Fatal("did not fail upon creation of an empty folder") } @@ -762,7 +762,7 @@ func TestGcsMkdirAll(t *testing.T) { t.Run("success", func(t *testing.T) { dirName := filepath.Join(bucketName, "a/b/c") - err := gcsAfs.MkdirAll(dirName, 0755) + err := gcsAfs.MkdirAll(dirName, 0o755) if err != nil { t.Fatal(err) } @@ -774,7 +774,7 @@ func TestGcsMkdirAll(t *testing.T) { if !info.Mode().IsDir() { t.Errorf("%s: mode is not directory", filepath.Join(bucketName, "a")) } - if info.Mode() != os.ModeDir|0755 { + if info.Mode() != os.ModeDir|0o755 { t.Errorf("%s: wrong permissions, expected drwxr-xr-x, got %s", filepath.Join(bucketName, "a"), info.Mode()) } info, err = gcsAfs.Stat(filepath.Join(bucketName, "a/b")) @@ -784,7 +784,7 @@ func TestGcsMkdirAll(t *testing.T) { if !info.Mode().IsDir() { t.Errorf("%s: mode is not directory", filepath.Join(bucketName, "a/b")) } - if info.Mode() != os.ModeDir|0755 { + if info.Mode() != os.ModeDir|0o755 { t.Errorf("%s: wrong permissions, expected drwxr-xr-x, got %s", filepath.Join(bucketName, "a/b"), info.Mode()) } info, err = gcsAfs.Stat(dirName) @@ -794,7 +794,7 @@ func TestGcsMkdirAll(t *testing.T) { if !info.Mode().IsDir() { t.Errorf("%s: mode is not directory", dirName) } - if info.Mode() != os.ModeDir|0755 { + if info.Mode() != os.ModeDir|0o755 { t.Errorf("%s: wrong permissions, expected drwxr-xr-x, got %s", dirName, info.Mode()) } @@ -816,7 +816,7 @@ func TestGcsRemoveAll(t *testing.T) { aDir := filepath.Join(bucketName, "a") bDir := filepath.Join(aDir, "b") - err := gcsAfs.MkdirAll(bDir, 0755) + err := gcsAfs.MkdirAll(bDir, 0o755) if err != nil { t.Fatal(err) } diff --git a/go.mod b/go.mod index a301a4ec..d6b1962e 100644 --- a/go.mod +++ b/go.mod @@ -4,9 +4,9 @@ require ( cloud.google.com/go/storage v1.14.0 github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8 github.com/pkg/sftp v1.13.1 - golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa + golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99 - golang.org/x/text v0.3.4 + golang.org/x/text v0.3.7 google.golang.org/api v0.40.0 ) diff --git a/go.sum b/go.sum index 3b9968a5..e0228c2c 100644 --- a/go.sum +++ b/go.sum @@ -163,8 +163,8 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa h1:idItI2DDfCokpg0N51B2VtiLdJ4vAuXC9fnCb2gACo4= -golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -230,8 +230,9 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -284,6 +285,7 @@ golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -294,8 +296,10 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/httpFs.go b/httpFs.go index 2b86e30d..ac0de6d5 100644 --- a/httpFs.go +++ b/httpFs.go @@ -29,7 +29,7 @@ type httpDir struct { } func (d httpDir) Open(name string) (http.File, error) { - if filepath.Separator != '/' && strings.IndexRune(name, filepath.Separator) >= 0 || + if filepath.Separator != '/' && strings.ContainsRune(name, filepath.Separator) || strings.Contains(name, "\x00") { return nil, errors.New("http: invalid character in file path") } diff --git a/internal/common/adapters.go b/internal/common/adapters.go new file mode 100644 index 00000000..60685caa --- /dev/null +++ b/internal/common/adapters.go @@ -0,0 +1,27 @@ +// Copyright © 2022 Steve Francia . +// +// 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 common + +import "io/fs" + +// FileInfoDirEntry provides an adapter from os.FileInfo to fs.DirEntry +type FileInfoDirEntry struct { + fs.FileInfo +} + +var _ fs.DirEntry = FileInfoDirEntry{} + +func (d FileInfoDirEntry) Type() fs.FileMode { return d.FileInfo.Mode().Type() } + +func (d FileInfoDirEntry) Info() (fs.FileInfo, error) { return d.FileInfo, nil } diff --git a/iofs.go b/iofs.go index c8034553..938b9316 100644 --- a/iofs.go +++ b/iofs.go @@ -1,3 +1,4 @@ +//go:build go1.16 // +build go1.16 package afero @@ -7,7 +8,10 @@ import ( "io/fs" "os" "path" + "sort" "time" + + "github.com/spf13/afero/internal/common" ) // IOFS adopts afero.Fs to stdlib io/fs.FS @@ -66,14 +70,31 @@ func (iofs IOFS) Glob(pattern string) ([]string, error) { } func (iofs IOFS) ReadDir(name string) ([]fs.DirEntry, error) { - items, err := ReadDir(iofs.Fs, name) + f, err := iofs.Fs.Open(name) if err != nil { return nil, iofs.wrapError("readdir", name, err) } + defer f.Close() + + if rdf, ok := f.(fs.ReadDirFile); ok { + items, err := rdf.ReadDir(-1) + if err != nil { + return nil, iofs.wrapError("readdir", name, err) + } + sort.Slice(items, func(i, j int) bool { return items[i].Name() < items[j].Name() }) + return items, nil + } + + items, err := f.Readdir(-1) + if err != nil { + return nil, iofs.wrapError("readdir", name, err) + } + sort.Sort(byName(items)) + ret := make([]fs.DirEntry, len(items)) for i := range items { - ret[i] = dirEntry{items[i]} + ret[i] = common.FileInfoDirEntry{FileInfo: items[i]} } return ret, nil @@ -108,17 +129,6 @@ func (IOFS) wrapError(op, path string, err error) error { } } -// dirEntry provides adapter from os.FileInfo to fs.DirEntry -type dirEntry struct { - fs.FileInfo -} - -var _ fs.DirEntry = dirEntry{} - -func (d dirEntry) Type() fs.FileMode { return d.FileInfo.Mode().Type() } - -func (d dirEntry) Info() (fs.FileInfo, error) { return d.FileInfo, nil } - // readDirFile provides adapter from afero.File to fs.ReadDirFile needed for correct Open type readDirFile struct { File @@ -134,7 +144,7 @@ func (r readDirFile) ReadDir(n int) ([]fs.DirEntry, error) { ret := make([]fs.DirEntry, len(items)) for i := range items { - ret[i] = dirEntry{items[i]} + ret[i] = common.FileInfoDirEntry{FileInfo: items[i]} } return ret, nil diff --git a/iofs_test.go b/iofs_test.go index 1d310e54..829e9191 100644 --- a/iofs_test.go +++ b/iofs_test.go @@ -1,3 +1,4 @@ +//go:build go1.16 // +build go1.16 package afero @@ -5,15 +6,25 @@ package afero import ( "bytes" "errors" + "fmt" "io" "io/fs" + "math/rand" "os" + "path/filepath" + "runtime" "testing" "testing/fstest" "time" + + "github.com/spf13/afero/internal/common" ) func TestIOFS(t *testing.T) { + if runtime.GOOS == "windows" { + // TODO(bep): some of the "bad path" tests in fstest.TestFS fail on Windows + t.Skip("Skipping on Windows") + } t.Parallel() t.Run("use MemMapFs", func(t *testing.T) { @@ -57,6 +68,94 @@ func TestIOFS(t *testing.T) { }) } +func TestIOFSNativeDirEntryWhenPossible(t *testing.T) { + t.Parallel() + + osfs := NewBasePathFs(NewOsFs(), t.TempDir()) + + err := osfs.MkdirAll("dir1/dir2", os.ModePerm) + if err != nil { + t.Fatal(err) + } + + const numFiles = 10 + + var fileNumbers []int + for i := 0; i < numFiles; i++ { + fileNumbers = append(fileNumbers, i) + } + rand.Shuffle(len(fileNumbers), func(i, j int) { + fileNumbers[i], fileNumbers[j] = fileNumbers[j], fileNumbers[i] + }) + + for _, i := range fileNumbers { + f, err := osfs.Create(fmt.Sprintf("dir1/dir2/test%d.txt", i)) + if err != nil { + t.Fatal(err) + } + f.Close() + } + + dir2, err := osfs.Open("dir1/dir2") + if err != nil { + t.Fatal(err) + } + defer dir2.Close() + + assertDirEntries := func(entries []fs.DirEntry, ordered bool) { + if len(entries) != numFiles { + t.Fatalf("expected %d, got %d", numFiles, len(entries)) + } + for i, entry := range entries { + if _, ok := entry.(common.FileInfoDirEntry); ok { + t.Fatal("DirEntry not native") + } + if ordered && entry.Name() != fmt.Sprintf("test%d.txt", i) { + t.Fatalf("expected %s, got %s", fmt.Sprintf("test%d.txt", i), entry.Name()) + } + } + } + + dirEntries, err := dir2.(fs.ReadDirFile).ReadDir(-1) + if err != nil { + t.Fatal(err) + } + assertDirEntries(dirEntries, false) + + iofs := NewIOFS(osfs) + + dirEntries, err = iofs.ReadDir("dir1/dir2") + if err != nil { + t.Fatal(err) + } + assertDirEntries(dirEntries, true) + + fileCount := 0 + err = fs.WalkDir(iofs, "", func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + + if !d.IsDir() { + fileCount++ + } + + if _, ok := d.(common.FileInfoDirEntry); ok { + t.Fatal("DirEntry not native") + } + + return nil + }) + + if err != nil { + t.Fatal(err) + } + + if fileCount != numFiles { + t.Fatalf("expected %d, got %d", numFiles, fileCount) + } +} + func TestFromIOFS(t *testing.T) { t.Parallel() @@ -259,7 +358,6 @@ func TestFromIOFS_File(t *testing.T) { // MapFS files implements io.ReaderAt b := make([]byte, 2) _, err := file.ReadAt(b, 2) - if err != nil { t.Errorf("ReadAt failed: %v", err) return @@ -319,7 +417,7 @@ func TestFromIOFS_File(t *testing.T) { return } - var expectedItems = []struct { + expectedItems := []struct { Name string IsDir bool Size int64 @@ -371,7 +469,7 @@ func TestFromIOFS_File(t *testing.T) { return } - var expectedItems = []string{"dir1", "dir2", "test.txt"} + expectedItems := []string{"dir1", "dir2", "test.txt"} if len(expectedItems) != len(items) { t.Errorf("Items count mismatch, expected %d, got %d", len(expectedItems), len(items)) @@ -410,3 +508,45 @@ func assertPermissionError(t *testing.T, err error) { t.Errorf("Expected (*fs.PathError).Err == fs.ErrPermisson, got %[1]T (%[1]v)", err) } } + +func BenchmarkWalkDir(b *testing.B) { + osfs := NewBasePathFs(NewOsFs(), b.TempDir()) + + createSomeFiles := func(dirname string) { + for i := 0; i < 10; i++ { + f, err := osfs.Create(filepath.Join(dirname, fmt.Sprintf("test%d.txt", i))) + if err != nil { + b.Fatal(err) + } + f.Close() + } + } + + depth := 10 + for level := depth; level > 0; level-- { + dirname := "" + for i := 0; i < level; i++ { + dirname = filepath.Join(dirname, fmt.Sprintf("dir%d", i)) + err := osfs.MkdirAll(dirname, 0o755) + if err != nil && !os.IsExist(err) { + b.Fatal(err) + } + } + createSomeFiles(dirname) + } + + iofs := NewIOFS(osfs) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + err := fs.WalkDir(iofs, "", func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + return nil + }) + if err != nil { + b.Fatal(err) + } + } +} diff --git a/ioutil.go b/ioutil.go index a403133e..fa6abe1e 100644 --- a/ioutil.go +++ b/ioutil.go @@ -141,8 +141,10 @@ func WriteFile(fs Fs, filename string, data []byte, perm os.FileMode) error { // 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 +var ( + randNum uint32 + randmu sync.Mutex +) func reseed() uint32 { return uint32(time.Now().UnixNano() + int64(os.Getpid())) @@ -150,12 +152,12 @@ func reseed() uint32 { func nextRandom() string { randmu.Lock() - r := rand + r := randNum if r == 0 { r = reseed() } r = r*1664525 + 1013904223 // constants from Numerical Recipes - rand = r + randNum = r randmu.Unlock() return strconv.Itoa(int(1e9 + r%1e9))[1:] } @@ -190,11 +192,11 @@ func TempFile(fs Fs, dir, pattern string) (f File, err error) { nconflict := 0 for i := 0; i < 10000; i++ { name := filepath.Join(dir, prefix+nextRandom()+suffix) - f, err = fs.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600) + f, err = fs.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0o600) if os.IsExist(err) { if nconflict++; nconflict > 10 { randmu.Lock() - rand = reseed() + randNum = reseed() randmu.Unlock() } continue @@ -214,6 +216,7 @@ func TempFile(fs Fs, dir, pattern string) (f File, err error) { 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() @@ -222,11 +225,11 @@ func TempDir(fs Fs, dir, prefix string) (name string, err error) { nconflict := 0 for i := 0; i < 10000; i++ { try := filepath.Join(dir, prefix+nextRandom()) - err = fs.Mkdir(try, 0700) + err = fs.Mkdir(try, 0o700) if os.IsExist(err) { if nconflict++; nconflict > 10 { randmu.Lock() - rand = reseed() + randNum = reseed() randmu.Unlock() } continue diff --git a/ioutil_test.go b/ioutil_test.go index 892705b0..004c66ba 100644 --- a/ioutil_test.go +++ b/ioutil_test.go @@ -37,13 +37,13 @@ func TestReadFile(t *testing.T) { testFS.Create("this_exists.go") filename := "rumpelstilzchen" - contents, err := fsutil.ReadFile(filename) + _, err := fsutil.ReadFile(filename) if err == nil { t.Fatalf("ReadFile %s: error expected, none found", filename) } filename = "this_exists.go" - contents, err = fsutil.ReadFile(filename) + contents, err := fsutil.ReadFile(filename) if err != nil { t.Fatalf("ReadFile %s: %v", filename, err) } @@ -63,7 +63,7 @@ func TestWriteFile(t *testing.T) { "build bigger and better idiot-proof programs, and the Universe trying " + "to produce bigger and better idiots. So far, the Universe is winning." - if err := fsutil.WriteFile(filename, []byte(data), 0644); err != nil { + if err := fsutil.WriteFile(filename, []byte(data), 0o644); err != nil { t.Fatalf("WriteFile %s: %v", filename, err) } @@ -83,7 +83,7 @@ func TestWriteFile(t *testing.T) { func TestReadDir(t *testing.T) { testFS = &MemMapFs{} - testFS.Mkdir("/i-am-a-dir", 0777) + testFS.Mkdir("/i-am-a-dir", 0o777) testFS.Create("/this_exists.go") dirname := "rumpelstilzchen" _, err := ReadDir(testFS, dirname) diff --git a/lstater_test.go b/lstater_test.go index 477924f6..cb5585f9 100644 --- a/lstater_test.go +++ b/lstater_test.go @@ -50,8 +50,8 @@ func TestLstatIfPossible(t *testing.T) { pathFileMem := filepath.Join(memWorkDir, "aferom.txt") - WriteFile(osFs, filepath.Join(workDir, "afero.txt"), []byte("Hi, Afero!"), 0777) - WriteFile(memFs, filepath.Join(pathFileMem), []byte("Hi, Afero!"), 0777) + WriteFile(osFs, filepath.Join(workDir, "afero.txt"), []byte("Hi, Afero!"), 0o777) + WriteFile(memFs, filepath.Join(pathFileMem), []byte("Hi, Afero!"), 0o777) os.Chdir(workDir) if err := os.Symlink("afero.txt", "symafero.txt"); err != nil { diff --git a/match_test.go b/match_test.go index fa2c17b5..3f24b58d 100644 --- a/match_test.go +++ b/match_test.go @@ -44,7 +44,7 @@ func setupGlobDirReusePath(t *testing.T, fs Fs, path string) string { func setupGlobFiles(t *testing.T, fs Fs, path string) string { testSubDir := filepath.Join(path, "globs", "bobs") - err := fs.MkdirAll(testSubDir, 0700) + err := fs.MkdirAll(testSubDir, 0o700) if err != nil && !os.IsExist(err) { t.Fatal(err) } @@ -84,7 +84,7 @@ func TestGlob(t *testing.T) { } } - var globTests = []struct { + globTests := []struct { pattern, result string }{ {testDir + "/globs/bobs/matcher", testDir + "/globs/bobs/matcher"}, @@ -136,7 +136,7 @@ func TestGlobSymlink(t *testing.T) { t.Skipf("skipping on %s", runtime.GOOS) } - var globSymlinkTests = []struct { + globSymlinkTests := []struct { path, dest string brokenLink bool }{ diff --git a/mem/file.go b/mem/file.go index 5ef8b6a3..62fe4498 100644 --- a/mem/file.go +++ b/mem/file.go @@ -18,15 +18,20 @@ import ( "bytes" "errors" "io" + "io/fs" "os" "path/filepath" "sync" "sync/atomic" "time" + + "github.com/spf13/afero/internal/common" ) const FilePathSeparator = string(filepath.Separator) +var _ fs.ReadDirFile = &File{} + type File struct { // atomic requires 64-bit alignment for struct field access at int64 @@ -183,10 +188,23 @@ func (f *File) Readdirnames(n int) (names []string, err error) { return names, err } +// Implements fs.ReadDirFile +func (f *File) ReadDir(n int) ([]fs.DirEntry, error) { + fi, err := f.Readdir(n) + if err != nil { + return nil, err + } + di := make([]fs.DirEntry, len(fi)) + for i, f := range fi { + di[i] = common.FileInfoDirEntry{FileInfo: f} + } + return di, nil +} + func (f *File) Read(b []byte) (n int, err error) { f.fileData.Lock() defer f.fileData.Unlock() - if f.closed == true { + if f.closed { return 0, ErrFileClosed } if len(b) > 0 && int(f.at) == len(f.fileData.data) { @@ -214,7 +232,7 @@ func (f *File) ReadAt(b []byte, off int64) (n int, err error) { } func (f *File) Truncate(size int64) error { - if f.closed == true { + if f.closed { return ErrFileClosed } if f.readOnly { @@ -227,7 +245,7 @@ func (f *File) Truncate(size int64) error { defer f.fileData.Unlock() 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))...) + f.fileData.data = append(f.fileData.data, bytes.Repeat([]byte{0o0}, int(diff))...) } else { f.fileData.data = f.fileData.data[0:size] } @@ -236,7 +254,7 @@ func (f *File) Truncate(size int64) error { } func (f *File) Seek(offset int64, whence int) (int64, error) { - if f.closed == true { + if f.closed { return 0, ErrFileClosed } switch whence { @@ -251,7 +269,7 @@ func (f *File) Seek(offset int64, whence int) (int64, error) { } func (f *File) Write(b []byte) (n int, err error) { - if f.closed == true { + if f.closed { return 0, ErrFileClosed } if f.readOnly { @@ -267,7 +285,7 @@ func (f *File) Write(b []byte) (n int, err error) { tail = f.fileData.data[n+int(cur):] } if diff > 0 { - f.fileData.data = append(f.fileData.data, append(bytes.Repeat([]byte{00}, int(diff)), b...)...) + f.fileData.data = append(f.fileData.data, append(bytes.Repeat([]byte{0o0}, int(diff)), b...)...) f.fileData.data = append(f.fileData.data, tail...) } else { f.fileData.data = append(f.fileData.data[:cur], b...) @@ -303,16 +321,19 @@ func (s *FileInfo) Name() string { 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() @@ -330,8 +351,8 @@ func (s *FileInfo) Size() int64 { var ( ErrFileClosed = errors.New("File is closed") - ErrOutOfRange = errors.New("Out of range") - ErrTooLarge = errors.New("Too large") + ErrOutOfRange = errors.New("out of range") + ErrTooLarge = errors.New("too large") ErrFileNotFound = os.ErrNotExist ErrFileExists = os.ErrExist ErrDestinationExists = os.ErrExist diff --git a/mem/file_test.go b/mem/file_test.go index 998a5d0b..2b123f32 100644 --- a/mem/file_test.go +++ b/mem/file_test.go @@ -66,8 +66,8 @@ func TestFileDataModTimeRace(t *testing.T) { func TestFileDataModeRace(t *testing.T) { t.Parallel() - const someMode = 0777 - const someOtherMode = 0660 + const someMode = 0o777 + const someOtherMode = 0o660 d := FileData{ mode: someMode, @@ -167,7 +167,7 @@ func TestFileDataIsDirRace(t *testing.T) { s.Unlock() }() - //just logging the value to trigger a read: + // just logging the value to trigger a read: t.Logf("Value is %v", s.IsDir()) } @@ -196,10 +196,10 @@ func TestFileDataSizeRace(t *testing.T) { s.Unlock() }() - //just logging the value to trigger a read: + // just logging the value to trigger a read: t.Logf("Value is %v", s.Size()) - //Testing the Dir size case + // Testing the Dir size case d.dir = true if s.Size() != int64(42) { t.Errorf("Failed to read correct value for dir, was %v", s.Size()) diff --git a/memmap.go b/memmap.go index d276a301..d6c744e8 100644 --- a/memmap.go +++ b/memmap.go @@ -15,14 +15,18 @@ package afero import ( "fmt" - "github.com/spf13/afero/mem" + "io" + "log" "os" "path/filepath" + "sort" "strings" "sync" "time" + + "github.com/spf13/afero/mem" ) const chmodBits = os.ModePerm | os.ModeSetuid | os.ModeSetgid | os.ModeSticky // Only a subset of bits are allowed to be changed. Documented under os.Chmod() @@ -43,7 +47,7 @@ func (m *MemMapFs) getData() map[string]*mem.FileData { // Root should always exist, right? // TODO: what about windows? root := mem.CreateDir(FilePathSeparator) - mem.SetMode(root, os.ModeDir|0755) + mem.SetMode(root, os.ModeDir|0o755) m.data[FilePathSeparator] = root }) return m.data @@ -114,12 +118,12 @@ func (m *MemMapFs) registerWithParent(f *mem.FileData, perm os.FileMode) { pdir := filepath.Dir(filepath.Clean(f.Name())) err := m.lockfreeMkdir(pdir, perm) if err != nil { - //log.Println("Mkdir error:", err) + // log.Println("Mkdir error:", err) return } parent, err = m.lockfreeOpen(pdir) if err != nil { - //log.Println("Open after Mkdir error:", err) + // log.Println("Open after Mkdir error:", err) return } } @@ -160,6 +164,11 @@ func (m *MemMapFs) Mkdir(name string, perm os.FileMode) error { } m.mu.Lock() + // Dobule check that it doesn't exist. + if _, ok := m.getData()[name]; ok { + m.mu.Unlock() + return &os.PathError{Op: "mkdir", Path: name, Err: ErrFileExists} + } item := mem.CreateDir(name) mem.SetMode(item, os.ModeDir|perm) m.getData()[name] = item @@ -250,7 +259,7 @@ func (m *MemMapFs) OpenFile(name string, flag int, perm os.FileMode) (File, erro file = mem.NewReadOnlyFileHandle(file.(*mem.File).Data()) } if flag&os.O_APPEND > 0 { - _, err = file.Seek(0, os.SEEK_END) + _, err = file.Seek(0, io.SeekEnd) if err != nil { file.Close() return nil, err @@ -308,49 +317,49 @@ func (m *MemMapFs) RemoveAll(path string) error { return nil } -func (m *MemMapFs) Rename(oldName, newName string) error { - oldName = normalizePath(oldName) - newName = normalizePath(newName) +func (m *MemMapFs) Rename(oldname, newname string) error { + oldname = normalizePath(oldname) + newname = normalizePath(newname) - if oldName == newName { + if oldname == newname { return nil } m.mu.RLock() defer m.mu.RUnlock() - if _, ok := m.getData()[oldName]; ok { + if _, ok := m.getData()[oldname]; ok { m.mu.RUnlock() m.mu.Lock() - err := m.unRegisterWithParent(oldName) + err := m.unRegisterWithParent(oldname) if err != nil { return err } - fileData := m.getData()[oldName] - mem.ChangeFileName(fileData, newName) - m.getData()[newName] = fileData + fileData := m.getData()[oldname] + mem.ChangeFileName(fileData, newname) + m.getData()[newname] = fileData - err = m.renameDescendants(oldName, newName) + err = m.renameDescendants(oldname, newname) if err != nil { return err } - delete(m.getData(), oldName) + delete(m.getData(), oldname) m.registerWithParent(fileData, 0) m.mu.Unlock() m.mu.RLock() } else { - return &os.PathError{Op: "rename", Path: oldName, Err: ErrFileNotFound} + return &os.PathError{Op: "rename", Path: oldname, Err: ErrFileNotFound} } return nil } -func (m *MemMapFs) renameDescendants(oldName, newName string) error { - descendants := m.findDescendants(oldName) +func (m *MemMapFs) renameDescendants(oldname, newname string) error { + descendants := m.findDescendants(oldname) removes := make([]string, 0, len(descendants)) for _, desc := range descendants { - descNewName := strings.Replace(desc.Name(), oldName, newName, 1) + descNewName := strings.Replace(desc.Name(), oldname, newname, 1) err := m.unRegisterWithParent(desc.Name()) if err != nil { return err diff --git a/memmap_test.go b/memmap_test.go index 486d25f0..6f778702 100644 --- a/memmap_test.go +++ b/memmap_test.go @@ -3,10 +3,12 @@ package afero import ( "fmt" "io" + "io/fs" "os" "path/filepath" "runtime" "strings" + "sync" "testing" "time" ) @@ -38,7 +40,7 @@ func TestPathErrors(t *testing.T) { path := filepath.Join(".", "some", "path") path2 := filepath.Join(".", "different", "path") fs := NewMemMapFs() - perm := os.FileMode(0755) + perm := os.FileMode(0o755) uid := 1000 gid := 1000 @@ -113,7 +115,7 @@ func checkPathError(t *testing.T, err error, op string) { // Ensure os.O_EXCL is correctly handled. func TestOpenFileExcl(t *testing.T) { const fileName = "/myFileTest" - const fileMode = os.FileMode(0765) + const fileMode = os.FileMode(0o765) fs := NewMemMapFs() @@ -139,7 +141,7 @@ func TestPermSet(t *testing.T) { const dirPath = "/myDirTest" const dirPathAll = "/my/path/to/dir" - const fileMode = os.FileMode(0765) + const fileMode = os.FileMode(0o765) // directories will also have the directory bit set const dirMode = fileMode | os.ModeDir @@ -202,7 +204,7 @@ func TestMultipleOpenFiles(t *testing.T) { defer removeAllTestFiles(t) const fileName = "afero-demo2.txt" - var data = make([][]byte, len(Fss)) + data := make([][]byte, len(Fss)) for i, fs := range Fss { dir := testDir(fs) @@ -215,16 +217,16 @@ func TestMultipleOpenFiles(t *testing.T) { if err != nil { t.Error("fh.Write failed: " + err.Error()) } - _, err = fh1.Seek(0, os.SEEK_SET) + _, err = fh1.Seek(0, io.SeekStart) if err != nil { t.Error(err) } - fh2, err := fs.OpenFile(path, os.O_RDWR, 0777) + fh2, err := fs.OpenFile(path, os.O_RDWR, 0o777) if err != nil { t.Error("fs.OpenFile failed: " + err.Error()) } - _, err = fh2.Seek(0, os.SEEK_END) + _, err = fh2.Seek(0, io.SeekEnd) if err != nil { t.Error(err) } @@ -293,7 +295,7 @@ func TestReadOnly(t *testing.T) { } f.Close() - f, err = fs.OpenFile(path, os.O_RDONLY, 0644) + f, err = fs.OpenFile(path, os.O_RDONLY, 0o644) if err != nil { t.Error("fs.Open failed: " + err.Error()) } @@ -384,7 +386,7 @@ func TestMemFsDataRace(t *testing.T) { const dir = "test_dir" fs := NewMemMapFs() - if err := fs.MkdirAll(dir, 0777); err != nil { + if err := fs.MkdirAll(dir, 0o777); err != nil { t.Fatal(err) } @@ -395,7 +397,7 @@ func TestMemFsDataRace(t *testing.T) { defer close(done) for i := 0; i < n; i++ { fname := filepath.Join(dir, fmt.Sprintf("%d.txt", i)) - if err := WriteFile(fs, fname, []byte(""), 0777); err != nil { + if err := WriteFile(fs, fname, []byte(""), 0o777); err != nil { panic(err) } if err := fs.Remove(fname); err != nil { @@ -440,7 +442,7 @@ func TestMemFsMkdirAllMode(t *testing.T) { t.Parallel() fs := NewMemMapFs() - err := fs.MkdirAll("/a/b/c", 0755) + err := fs.MkdirAll("/a/b/c", 0o755) if err != nil { t.Fatal(err) } @@ -454,7 +456,7 @@ func TestMemFsMkdirAllMode(t *testing.T) { if !info.ModTime().After(time.Now().Add(-1 * time.Hour)) { t.Errorf("/a: mod time not set, got %s", info.ModTime()) } - if info.Mode() != os.FileMode(os.ModeDir|0755) { + if info.Mode() != os.FileMode(os.ModeDir|0o755) { t.Errorf("/a: wrong permissions, expected drwxr-xr-x, got %s", info.Mode()) } info, err = fs.Stat("/a/b") @@ -464,7 +466,7 @@ func TestMemFsMkdirAllMode(t *testing.T) { if !info.Mode().IsDir() { t.Error("/a/b: mode is not directory") } - if info.Mode() != os.FileMode(os.ModeDir|0755) { + if info.Mode() != os.FileMode(os.ModeDir|0o755) { t.Errorf("/a/b: wrong permissions, expected drwxr-xr-x, got %s", info.Mode()) } if !info.ModTime().After(time.Now().Add(-1 * time.Hour)) { @@ -477,7 +479,7 @@ func TestMemFsMkdirAllMode(t *testing.T) { if !info.Mode().IsDir() { t.Error("/a/b/c: mode is not directory") } - if info.Mode() != os.FileMode(os.ModeDir|0755) { + if info.Mode() != os.FileMode(os.ModeDir|0o755) { t.Errorf("/a/b/c: wrong permissions, expected drwxr-xr-x, got %s", info.Mode()) } if !info.ModTime().After(time.Now().Add(-1 * time.Hour)) { @@ -490,7 +492,7 @@ func TestMemFsMkdirAllNoClobber(t *testing.T) { t.Parallel() fs := NewMemMapFs() - err := fs.MkdirAll("/a/b/c", 0755) + err := fs.MkdirAll("/a/b/c", 0o755) if err != nil { t.Fatal(err) } @@ -498,10 +500,10 @@ func TestMemFsMkdirAllNoClobber(t *testing.T) { if err != nil { t.Fatal(err) } - if info.Mode() != os.FileMode(os.ModeDir|0755) { + if info.Mode() != os.FileMode(os.ModeDir|0o755) { t.Errorf("/a/b: wrong permissions, expected drwxr-xr-x, got %s", info.Mode()) } - err = fs.MkdirAll("/a/b/c/d/e/f", 0710) + err = fs.MkdirAll("/a/b/c/d/e/f", 0o710) // '/a/b' is unchanged if err != nil { t.Fatal(err) @@ -510,7 +512,7 @@ func TestMemFsMkdirAllNoClobber(t *testing.T) { if err != nil { t.Fatal(err) } - if info.Mode() != os.FileMode(os.ModeDir|0755) { + if info.Mode() != os.FileMode(os.ModeDir|0o755) { t.Errorf("/a/b: wrong permissions, expected drwxr-xr-x, got %s", info.Mode()) } // new directories created with proper permissions @@ -518,32 +520,32 @@ func TestMemFsMkdirAllNoClobber(t *testing.T) { if err != nil { t.Fatal(err) } - if info.Mode() != os.FileMode(os.ModeDir|0710) { + if info.Mode() != os.FileMode(os.ModeDir|0o710) { t.Errorf("/a/b/c/d: wrong permissions, expected drwx--x---, got %s", info.Mode()) } info, err = fs.Stat("/a/b/c/d/e") if err != nil { t.Fatal(err) } - if info.Mode() != os.FileMode(os.ModeDir|0710) { + if info.Mode() != os.FileMode(os.ModeDir|0o710) { t.Errorf("/a/b/c/d/e: wrong permissions, expected drwx--x---, got %s", info.Mode()) } info, err = fs.Stat("/a/b/c/d/e/f") if err != nil { t.Fatal(err) } - if info.Mode() != os.FileMode(os.ModeDir|0710) { + if info.Mode() != os.FileMode(os.ModeDir|0o710) { t.Errorf("/a/b/c/d/e/f: wrong permissions, expected drwx--x---, got %s", info.Mode()) } } func TestMemFsDirMode(t *testing.T) { fs := NewMemMapFs() - err := fs.Mkdir("/testDir1", 0644) + err := fs.Mkdir("/testDir1", 0o644) if err != nil { t.Error(err) } - err = fs.MkdirAll("/sub/testDir2", 0644) + err = fs.MkdirAll("/sub/testDir2", 0o644) if err != nil { t.Error(err) } @@ -574,7 +576,7 @@ func TestMemFsUnexpectedEOF(t *testing.T) { fs := NewMemMapFs() - if err := WriteFile(fs, "file.txt", []byte("abc"), 0777); err != nil { + if err := WriteFile(fs, "file.txt", []byte("abc"), 0o777); err != nil { t.Fatal(err) } @@ -603,7 +605,7 @@ func TestMemFsChmod(t *testing.T) { fs := NewMemMapFs() const file = "hello" - if err := fs.Mkdir(file, 0700); err != nil { + if err := fs.Mkdir(file, 0o700); err != nil { t.Fatal(err) } @@ -634,7 +636,7 @@ func TestMemFsMkdirModeIllegal(t *testing.T) { t.Parallel() fs := NewMemMapFs() - err := fs.Mkdir("/a", os.ModeSocket|0755) + err := fs.Mkdir("/a", os.ModeSocket|0o755) if err != nil { t.Fatal(err) } @@ -642,7 +644,7 @@ func TestMemFsMkdirModeIllegal(t *testing.T) { if err != nil { t.Fatal(err) } - if info.Mode() != os.FileMode(os.ModeDir|0755) { + if info.Mode() != os.FileMode(os.ModeDir|0o755) { t.Fatalf("should not be able to use Mkdir to set illegal mode: %s", info.Mode().String()) } } @@ -652,7 +654,7 @@ func TestMemFsOpenFileModeIllegal(t *testing.T) { t.Parallel() fs := NewMemMapFs() - file, err := fs.OpenFile("/a", os.O_CREATE, os.ModeSymlink|0644) + file, err := fs.OpenFile("/a", os.O_CREATE, os.ModeSymlink|0o644) if err != nil { t.Fatal(err) } @@ -661,7 +663,7 @@ func TestMemFsOpenFileModeIllegal(t *testing.T) { if err != nil { t.Fatal(err) } - if info.Mode() != os.FileMode(0644) { + if info.Mode() != os.FileMode(0o644) { t.Fatalf("should not be able to use OpenFile to set illegal mode: %s", info.Mode().String()) } } @@ -694,6 +696,144 @@ func TestMemFsLstatIfPossible(t *testing.T) { } } +func TestMemMapFsConfurrentMkdir(t *testing.T) { + const dir = "test_dir" + const n = 1000 + mfs := NewMemMapFs().(*MemMapFs) + + allFilePaths := make([]string, 0, n) + + // run concurrency test + var wg sync.WaitGroup + for i := 0; i < n; i++ { + fp := filepath.Join( + dir, + fmt.Sprintf("%02d", n%10), + fmt.Sprintf("%d.txt", i), + ) + allFilePaths = append(allFilePaths, fp) + + wg.Add(1) + go func() { + defer wg.Done() + + if err := mfs.MkdirAll(filepath.Dir(fp), 0o755); err != nil { + t.Error(err) + } + + wt, err := mfs.Create(fp) + if err != nil { + t.Error(err) + } + defer func() { + if err := wt.Close(); err != nil { + t.Error(err) + } + }() + + // write 30 bytes + for j := 0; j < 10; j++ { + _, err := wt.Write([]byte("000")) + if err != nil { + t.Error(err) + } + } + }() + } + wg.Wait() + + // Test1: find all files by full path access + for _, fp := range allFilePaths { + info, err := mfs.Stat(fp) + if err != nil { + t.Error(err) + } + + if info.Size() != 30 { + t.Errorf("file size should be 30, but got %d", info.Size()) + } + + } + + // Test2: find all files by walk + foundFiles := make([]string, 0, n) + wErr := Walk(mfs, dir, func(path string, info fs.FileInfo, err error) error { + if err != nil { + t.Error(err) + } + if info.IsDir() { + return nil // skip dir + } + if strings.HasSuffix(info.Name(), ".txt") { + foundFiles = append(foundFiles, path) + } + return nil + }) + if wErr != nil { + t.Error(wErr) + } + if len(foundFiles) != n { + t.Errorf("found %d files, but expect %d", len(foundFiles), n) + } +} + +func TestMemFsRenameDir(t *testing.T) { + const srcPath = "/src" + const dstPath = "/dst" + const subDir = "dir" + const subFile = "file.txt" + + fs := NewMemMapFs() + + err := fs.MkdirAll(srcPath+FilePathSeparator+subDir, 0o777) + if err != nil { + t.Fatalf("MkDirAll failed: %s", err) + } + + f, err := fs.Create(srcPath + FilePathSeparator + subFile) + if err != nil { + t.Fatalf("Create failed: %s", err) + } + if err = f.Close(); err != nil { + t.Fatalf("Close failed: %s", err) + } + + err = fs.Rename(srcPath, dstPath) + if err != nil { + t.Fatalf("Rename failed: %s", err) + } + + _, err = fs.Stat(srcPath + FilePathSeparator + subDir) + if err == nil { + t.Fatalf("SubDir still exists in the source dir") + } + + _, err = fs.Stat(srcPath + FilePathSeparator + subFile) + if err == nil { + t.Fatalf("SubFile still exists in the source dir") + } + + _, err = fs.Stat(dstPath + FilePathSeparator + subDir) + if err != nil { + t.Fatalf("SubDir stat in the destination dir: %s", err) + } + + _, err = fs.Stat(dstPath + FilePathSeparator + subFile) + if err != nil { + t.Fatalf("SubFile stat in the destination dir: %s", err) + } + + err = fs.Mkdir(srcPath, 0o777) + if err != nil { + t.Fatalf("Cannot recreate the source dir: %s", err) + } + + err = fs.Mkdir(srcPath+FilePathSeparator+subDir, 0o777) + if err != nil { + t.Errorf("Cannot recreate the subdir in the source dir: %s", err) + } +} + func TestMemMapFsRename(t *testing.T) { t.Parallel() diff --git a/regexpfs.go b/regexpfs.go index ac359c62..218f3b23 100644 --- a/regexpfs.go +++ b/regexpfs.go @@ -10,7 +10,6 @@ import ( // 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 diff --git a/ro_regexp_test.go b/ro_regexp_test.go index ef8a35d0..3dc8930b 100644 --- a/ro_regexp_test.go +++ b/ro_regexp_test.go @@ -16,12 +16,12 @@ func TestFilterReadOnly(t *testing.T) { func TestFilterReadonlyRemoveAndRead(t *testing.T) { mfs := &MemMapFs{} - fh, err := mfs.Create("/file.txt") + fh, _ := mfs.Create("/file.txt") fh.Write([]byte("content here")) fh.Close() fs := NewReadOnlyFs(mfs) - err = fs.Remove("/file.txt") + err := fs.Remove("/file.txt") if err == nil { t.Errorf("Did not fail to remove file") } @@ -54,7 +54,6 @@ func TestFilterRegexp(t *testing.T) { fs := NewRegexpFs(&MemMapFs{}, regexp.MustCompile(`\.txt$`)) _, err := fs.Create("/file.html") if err == nil { - t.Errorf("Did not fail to create file") } // t.Logf("ERR=%s", err) @@ -75,7 +74,7 @@ func TestFilterRegexReadDir(t *testing.T) { fs1 := &RegexpFs{re: regexp.MustCompile(`\.txt$`), source: mfs} fs := &RegexpFs{re: regexp.MustCompile(`^a`), source: fs1} - mfs.MkdirAll("/dir/sub", 0777) + mfs.MkdirAll("/dir/sub", 0o777) for _, name := range []string{"afile.txt", "afile.html", "bfile.txt"} { for _, dir := range []string{"/dir/", "/dir/sub/"} { fh, _ := mfs.Create(dir + name) diff --git a/sftpfs/file.go b/sftpfs/file.go index eef14e36..324282f9 100644 --- a/sftpfs/file.go +++ b/sftpfs/file.go @@ -64,9 +64,8 @@ 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 + return f.fd.ReadAt(b, off) } func (f *File) Readdir(count int) (res []os.FileInfo, err error) { diff --git a/sftpfs/sftp_test.go b/sftpfs/sftp_test.go index 4dba7fce..bba2f361 100644 --- a/sftpfs/sftp_test.go +++ b/sftpfs/sftp_test.go @@ -215,6 +215,9 @@ func MakeSSHKeyPair(bits int, pubKeyPath, privateKeyPath string) error { // generate and write private key as PEM privateKeyFile, err := os.Create(privateKeyPath) + if err != nil { + return err + } defer privateKeyFile.Close() if err != nil { return err @@ -231,11 +234,11 @@ func MakeSSHKeyPair(bits int, pubKeyPath, privateKeyPath string) error { return err } - return ioutil.WriteFile(pubKeyPath, ssh.MarshalAuthorizedKey(pub), 0655) + return ioutil.WriteFile(pubKeyPath, ssh.MarshalAuthorizedKey(pub), 0o655) } func TestSftpCreate(t *testing.T) { - os.Mkdir("./test", 0777) + os.Mkdir("./test", 0o777) MakeSSHKeyPair(1024, "./test/id_rsa.pub", "./test/id_rsa") go RunSftpServer("./test/") @@ -247,12 +250,12 @@ func TestSftpCreate(t *testing.T) { } defer ctx.Disconnect() - var fs = New(ctx.sftpc) + fs := New(ctx.sftpc) - fs.MkdirAll("test/dir1/dir2/dir3", os.FileMode(0777)) - fs.Mkdir("test/foo", os.FileMode(0000)) - fs.Chmod("test/foo", os.FileMode(0700)) - fs.Mkdir("test/bar", os.FileMode(0777)) + fs.MkdirAll("test/dir1/dir2/dir3", os.FileMode(0o777)) + fs.Mkdir("test/foo", os.FileMode(0o000)) + fs.Chmod("test/foo", os.FileMode(0o700)) + fs.Mkdir("test/bar", os.FileMode(0o777)) file, err := fs.Create("file1") if err != nil { diff --git a/symlink.go b/symlink.go index d1c6ea53..aa6ae125 100644 --- a/symlink.go +++ b/symlink.go @@ -21,9 +21,9 @@ import ( // filesystems saying so. // It indicates support for 3 symlink related interfaces that implement the // behaviors of the os methods: -// - Lstat -// - Symlink, and -// - Readlink +// - Lstat +// - Symlink, and +// - Readlink type Symlinker interface { Lstater Linker diff --git a/symlink_test.go b/symlink_test.go index 5a9e5db6..77dd742f 100644 --- a/symlink_test.go +++ b/symlink_test.go @@ -38,14 +38,14 @@ func TestSymlinkIfPossible(t *testing.T) { pathFileMem := filepath.Join(memWorkDir, "aferom.txt") osPath := filepath.Join(workDir, "afero.txt") - WriteFile(osFs, osPath, []byte("Hi, Afero!"), 0777) - WriteFile(memFs, filepath.Join(pathFileMem), []byte("Hi, Afero!"), 0777) + WriteFile(osFs, osPath, []byte("Hi, Afero!"), 0o777) + WriteFile(memFs, filepath.Join(pathFileMem), []byte("Hi, Afero!"), 0o777) testLink := func(l Linker, source, destination string, output *string) { if fs, ok := l.(Fs); ok { dir := filepath.Dir(destination) if dir != "" { - fs.MkdirAll(dir, 0777) + fs.MkdirAll(dir, 0o777) } } @@ -117,14 +117,14 @@ func TestReadlinkIfPossible(t *testing.T) { pathFileMem := filepath.Join(memWorkDir, "aferom.txt") osPath := filepath.Join(workDir, "afero.txt") - WriteFile(osFs, osPath, []byte("Hi, Afero!"), 0777) - WriteFile(memFs, filepath.Join(pathFileMem), []byte("Hi, Afero!"), 0777) + WriteFile(osFs, osPath, []byte("Hi, Afero!"), 0o777) + WriteFile(memFs, filepath.Join(pathFileMem), []byte("Hi, Afero!"), 0o777) createLink := func(l Linker, source, destination string) error { if fs, ok := l.(Fs); ok { dir := filepath.Dir(destination) if dir != "" { - fs.MkdirAll(dir, 0777) + fs.MkdirAll(dir, 0o777) } } diff --git a/tarfs/tarfs_test.go b/tarfs/tarfs_test.go index c588c792..228ed9b4 100644 --- a/tarfs/tarfs_test.go +++ b/tarfs/tarfs_test.go @@ -161,7 +161,7 @@ func TestSeek(t *testing.T) { t.Fatalf("opening %v: %v", f.name, err) } - var tests = []struct { + tests := []struct { offin int64 whence int offout int64 @@ -252,7 +252,7 @@ func TestClose(t *testing.T) { func TestOpenFile(t *testing.T) { for _, f := range files { - file, err := afs.OpenFile(f.name, os.O_RDONLY, 0400) + file, err := afs.OpenFile(f.name, os.O_RDONLY, 0o400) if !f.exists { if !errors.Is(err, syscall.ENOENT) { t.Errorf("%v: got %v, expected%v", f.name, err, syscall.ENOENT) @@ -266,7 +266,7 @@ func TestOpenFile(t *testing.T) { } file.Close() - file, err = afs.OpenFile(f.name, os.O_CREATE, 0600) + _, err = afs.OpenFile(f.name, os.O_CREATE, 0o600) if !errors.Is(err, syscall.EPERM) { t.Errorf("%v: open for write: got %v, expected %v", f.name, err, syscall.EPERM) } diff --git a/unionFile.go b/unionFile.go index 34f99a40..62dd6c93 100644 --- a/unionFile.go +++ b/unionFile.go @@ -47,7 +47,7 @@ func (f *UnionFile) Read(s []byte) (int, error) { 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), io.SeekCurrent); seekErr != nil { // only overwrite err in case the seek fails: we need to // report an eventual io.EOF to the caller err = seekErr @@ -65,7 +65,7 @@ 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) + _, err = f.Base.Seek(o+int64(n), io.SeekStart) } return n, err } @@ -130,7 +130,7 @@ func (f *UnionFile) Name() string { 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) + files := make(map[string]os.FileInfo) for _, fi := range lofi { files[fi.Name()] = fi @@ -151,7 +151,6 @@ var defaultUnionMergeDirsFn = func(lofi, bofi []os.FileInfo) ([]os.FileInfo, err } return rfi, nil - } // Readdir will weave the two directories together and @@ -275,7 +274,7 @@ func copyFile(base Fs, layer Fs, name string, bfh File) error { return err } if !exists { - err = layer.MkdirAll(filepath.Dir(name), 0777) // FIXME? + err = layer.MkdirAll(filepath.Dir(name), 0o777) // FIXME? if err != nil { return err } diff --git a/util.go b/util.go index 4f253f48..9e4cba27 100644 --- a/util.go +++ b/util.go @@ -25,6 +25,7 @@ import ( "strings" "unicode" + "golang.org/x/text/runes" "golang.org/x/text/transform" "golang.org/x/text/unicode/norm" ) @@ -42,7 +43,7 @@ func WriteReader(fs Fs, path string, r io.Reader) (err error) { ospath := filepath.FromSlash(dir) if ospath != "" { - err = fs.MkdirAll(ospath, 0777) // rwx, rw, r + err = fs.MkdirAll(ospath, 0o777) // rwx, rw, r if err != nil { if err != os.ErrExist { return err @@ -70,7 +71,7 @@ func SafeWriteReader(fs Fs, path string, r io.Reader) (err error) { ospath := filepath.FromSlash(dir) if ospath != "" { - err = fs.MkdirAll(ospath, 0777) // rwx, rw, r + err = fs.MkdirAll(ospath, 0o777) // rwx, rw, r if err != nil { return } @@ -123,7 +124,7 @@ func GetTempDir(fs Fs, subPath string) string { return addSlash(dir) } - err := fs.MkdirAll(dir, 0777) + err := fs.MkdirAll(dir, 0o777) if err != nil { panic(err) } @@ -158,16 +159,12 @@ func UnicodeSanitize(s string) string { // Transform characters with accents into plain forms. func NeuterAccents(s string) string { - t := transform.Chain(norm.NFD, transform.RemoveFunc(isMn), norm.NFC) + t := transform.Chain(norm.NFD, runes.Remove(runes.In(unicode.Mn)), 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) } @@ -200,7 +197,6 @@ func FileContainsAnyBytes(fs Fs, filename string, subslices [][]byte) (bool, err // 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 } @@ -299,6 +295,9 @@ func IsEmpty(fs Fs, path string) (bool, error) { } defer f.Close() list, err := f.Readdir(-1) + if err != nil { + return false, err + } return len(list) == 0, nil } return fi.Size() == 0, nil diff --git a/util_test.go b/util_test.go index d2145cf9..13ec23b1 100644 --- a/util_test.go +++ b/util_test.go @@ -36,8 +36,8 @@ func TestDirExists(t *testing.T) { } // First create a couple directories so there is something in the filesystem - //testFS := new(MemMapFs) - testFS.MkdirAll("/foo/bar", 0777) + // testFS := new(MemMapFs) + testFS.MkdirAll("/foo/bar", 0o777) data := []test{ {".", true}, @@ -155,7 +155,8 @@ func TestReaderContains(t *testing.T) { {"", nil, false}, {"", [][]byte{[]byte("a")}, false}, {"a", [][]byte{[]byte("")}, false}, - {"", [][]byte{[]byte("")}, false}} { + {"", [][]byte{[]byte("")}, false}, + } { result := readerContainsAny(strings.NewReader(this.v1), this.v2...) if result != this.expect { t.Errorf("[%d] readerContains: got %t but expected %t", i, result, this.expect) @@ -185,10 +186,10 @@ func createZeroSizedFileInTempDir() (File, error) { func createNonZeroSizedFileInTempDir() (File, error) { f, err := createZeroSizedFileInTempDir() if err != nil { - // no file ?? + return nil, err } byteString := []byte("byteString") - err = WriteFile(testFS, f.Name(), byteString, 0644) + err = WriteFile(testFS, f.Name(), byteString, 0o644) if err != nil { // delete the file deleteFileInTempDir(f) @@ -200,7 +201,7 @@ func createNonZeroSizedFileInTempDir() (File, error) { func deleteFileInTempDir(f File) { err := testFS.Remove(f.Name()) if err != nil { - // now what? + panic(err) } } @@ -217,7 +218,7 @@ func createEmptyTempDir() (string, error) { func createTempDirWithZeroLengthFiles() (string, error) { d, dirErr := createEmptyTempDir() if dirErr != nil { - //now what? + return "", dirErr } filePrefix := "_path_test_" _, fileErr := TempFile(testFS, d, filePrefix) // dir is os.TempDir() @@ -229,13 +230,12 @@ func createTempDirWithZeroLengthFiles() (string, error) { } // the dir now has one, zero length file in it return d, nil - } func createTempDirWithNonZeroLengthFiles() (string, error) { d, dirErr := createEmptyTempDir() if dirErr != nil { - //now what? + return "", dirErr } filePrefix := "_path_test_" f, fileErr := TempFile(testFS, d, filePrefix) // dir is os.TempDir() @@ -246,7 +246,7 @@ func createTempDirWithNonZeroLengthFiles() (string, error) { return "", fileErr } byteString := []byte("byteString") - fileErr = WriteFile(testFS, f.Name(), byteString, 0644) + fileErr = WriteFile(testFS, f.Name(), byteString, 0o644) if fileErr != nil { // delete the file deleteFileInTempDir(f) @@ -257,7 +257,6 @@ func createTempDirWithNonZeroLengthFiles() (string, error) { // the dir now has one, zero length file in it return d, nil - } func TestExists(t *testing.T) { @@ -292,7 +291,6 @@ func TestExists(t *testing.T) { t.Errorf("Test %d failed. Expected %q got %q", i, d.expectedErr, err) } } - } func TestSafeWriteToDisk(t *testing.T) { @@ -406,7 +404,7 @@ func TestGetTempDir(t *testing.T) { func deleteTempDir(d string) { err := os.RemoveAll(d) if err != nil { - // now what? + panic(err) } } diff --git a/zipfs/file.go b/zipfs/file.go index 355f5f45..9b7ddbef 100644 --- a/zipfs/file.go +++ b/zipfs/file.go @@ -85,10 +85,10 @@ func (f *File) Seek(offset int64, whence int) (int64, error) { return 0, afero.ErrFileClosed } switch whence { - case os.SEEK_SET: - case os.SEEK_CUR: + case io.SeekStart: + case io.SeekCurrent: offset += f.offset - case os.SEEK_END: + case io.SeekEnd: offset += int64(f.zipfile.UncompressedSize64) default: return 0, syscall.EINVAL diff --git a/zipfs/zipfs_test.go b/zipfs/zipfs_test.go index bf781cc0..6fb9743f 100644 --- a/zipfs/zipfs_test.go +++ b/zipfs/zipfs_test.go @@ -1,12 +1,12 @@ package zipfs import ( - "github.com/spf13/afero" - "archive/zip" "path/filepath" "reflect" "testing" + + "github.com/spf13/afero" ) func TestZipFS(t *testing.T) {