2021-04-18 15:41:13 -04:00
|
|
|
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
|
|
//
|
|
|
|
// This file is part of MinIO Object Storage stack
|
|
|
|
//
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU Affero General Public License as published by
|
|
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
|
|
// (at your option) any later version.
|
|
|
|
//
|
|
|
|
// This program is distributed in the hope that it will be useful
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU Affero General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
2017-01-16 20:05:00 -05:00
|
|
|
|
|
|
|
package cmd
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2017-08-03 23:04:28 -04:00
|
|
|
"io"
|
2017-04-14 15:06:24 -04:00
|
|
|
"io/ioutil"
|
|
|
|
"os"
|
|
|
|
"path"
|
2017-01-16 20:05:00 -05:00
|
|
|
"testing"
|
2017-04-14 15:06:24 -04:00
|
|
|
|
|
|
|
"github.com/minio/minio/pkg/lock"
|
2017-01-16 20:05:00 -05:00
|
|
|
)
|
|
|
|
|
2017-05-05 11:49:09 -04:00
|
|
|
func TestFSRenameFile(t *testing.T) {
|
2020-06-12 23:04:01 -04:00
|
|
|
// create xlStorage test setup
|
|
|
|
_, path, err := newXLStorageTestSetup()
|
2017-05-05 11:49:09 -04:00
|
|
|
if err != nil {
|
2020-06-12 23:04:01 -04:00
|
|
|
t.Fatalf("Unable to create xlStorage test setup, %s", err)
|
2017-05-05 11:49:09 -04:00
|
|
|
}
|
2017-08-12 22:25:43 -04:00
|
|
|
defer os.RemoveAll(path)
|
2017-05-05 11:49:09 -04:00
|
|
|
|
2020-04-09 12:30:02 -04:00
|
|
|
if err = fsMkdir(GlobalContext, pathJoin(path, "testvolume1")); err != nil {
|
2017-05-05 11:49:09 -04:00
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2020-04-09 12:30:02 -04:00
|
|
|
if err = fsRenameFile(GlobalContext, pathJoin(path, "testvolume1"), pathJoin(path, "testvolume2")); err != nil {
|
2017-05-05 11:49:09 -04:00
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2020-04-09 12:30:02 -04:00
|
|
|
if err = fsRenameFile(GlobalContext, pathJoin(path, "testvolume1"), pathJoin(path, "testvolume2")); err != errFileNotFound {
|
2017-05-05 11:49:09 -04:00
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2020-04-09 12:30:02 -04:00
|
|
|
if err = fsRenameFile(GlobalContext, pathJoin(path, "my-obj-del-0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001"), pathJoin(path, "testvolume2")); err != errFileNameTooLong {
|
2017-05-05 11:49:09 -04:00
|
|
|
t.Fatal("Unexpected error", err)
|
|
|
|
}
|
2020-04-09 12:30:02 -04:00
|
|
|
if err = fsRenameFile(GlobalContext, pathJoin(path, "testvolume1"), pathJoin(path, "my-obj-del-0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001")); err != errFileNameTooLong {
|
2017-05-05 11:49:09 -04:00
|
|
|
t.Fatal("Unexpected error", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-16 20:05:00 -05:00
|
|
|
func TestFSStats(t *testing.T) {
|
2020-06-12 23:04:01 -04:00
|
|
|
// create xlStorage test setup
|
|
|
|
_, path, err := newXLStorageTestSetup()
|
2017-01-16 20:05:00 -05:00
|
|
|
if err != nil {
|
2020-06-12 23:04:01 -04:00
|
|
|
t.Fatalf("Unable to create xlStorage test setup, %s", err)
|
2017-01-16 20:05:00 -05:00
|
|
|
}
|
2017-08-12 22:25:43 -04:00
|
|
|
defer os.RemoveAll(path)
|
2017-01-16 20:05:00 -05:00
|
|
|
|
|
|
|
// Setup test environment.
|
|
|
|
|
2020-04-09 12:30:02 -04:00
|
|
|
if err = fsMkdir(GlobalContext, ""); err != errInvalidArgument {
|
2017-01-16 20:05:00 -05:00
|
|
|
t.Fatal("Unexpected error", err)
|
|
|
|
}
|
|
|
|
|
2020-04-09 12:30:02 -04:00
|
|
|
if err = fsMkdir(GlobalContext, pathJoin(path, "my-obj-del-0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001")); err != errFileNameTooLong {
|
2017-01-16 20:05:00 -05:00
|
|
|
t.Fatal("Unexpected error", err)
|
|
|
|
}
|
|
|
|
|
2020-04-09 12:30:02 -04:00
|
|
|
if err = fsMkdir(GlobalContext, pathJoin(path, "success-vol")); err != nil {
|
2017-01-16 20:05:00 -05:00
|
|
|
t.Fatalf("Unable to create volume, %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
var reader = bytes.NewReader([]byte("Hello, world"))
|
fix: use buffers only when necessary for io.Copy() (#11229)
Use separate sync.Pool for writes/reads
Avoid passing buffers for io.CopyBuffer()
if the writer or reader implement io.WriteTo or io.ReadFrom
respectively then its useless for sync.Pool to allocate
buffers on its own since that will be completely ignored
by the io.CopyBuffer Go implementation.
Improve this wherever we see this to be optimal.
This allows us to be more efficient on memory usage.
```
385 // copyBuffer is the actual implementation of Copy and CopyBuffer.
386 // if buf is nil, one is allocated.
387 func copyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error) {
388 // If the reader has a WriteTo method, use it to do the copy.
389 // Avoids an allocation and a copy.
390 if wt, ok := src.(WriterTo); ok {
391 return wt.WriteTo(dst)
392 }
393 // Similarly, if the writer has a ReadFrom method, use it to do the copy.
394 if rt, ok := dst.(ReaderFrom); ok {
395 return rt.ReadFrom(src)
396 }
```
From readahead package
```
// WriteTo writes data to w until there's no more data to write or when an error occurs.
// The return value n is the number of bytes written.
// Any error encountered during the write is also returned.
func (a *reader) WriteTo(w io.Writer) (n int64, err error) {
if a.err != nil {
return 0, a.err
}
n = 0
for {
err = a.fill()
if err != nil {
return n, err
}
n2, err := w.Write(a.cur.buffer())
a.cur.inc(n2)
n += int64(n2)
if err != nil {
return n, err
}
```
2021-01-06 12:36:55 -05:00
|
|
|
if _, err = fsCreateFile(GlobalContext, pathJoin(path, "success-vol", "success-file"), reader, 0); err != nil {
|
2017-01-16 20:05:00 -05:00
|
|
|
t.Fatalf("Unable to create file, %s", err)
|
|
|
|
}
|
|
|
|
// Seek back.
|
|
|
|
reader.Seek(0, 0)
|
|
|
|
|
2020-04-09 12:30:02 -04:00
|
|
|
if err = fsMkdir(GlobalContext, pathJoin(path, "success-vol", "success-file")); err != errVolumeExists {
|
2017-01-16 20:05:00 -05:00
|
|
|
t.Fatal("Unexpected error", err)
|
|
|
|
}
|
|
|
|
|
fix: use buffers only when necessary for io.Copy() (#11229)
Use separate sync.Pool for writes/reads
Avoid passing buffers for io.CopyBuffer()
if the writer or reader implement io.WriteTo or io.ReadFrom
respectively then its useless for sync.Pool to allocate
buffers on its own since that will be completely ignored
by the io.CopyBuffer Go implementation.
Improve this wherever we see this to be optimal.
This allows us to be more efficient on memory usage.
```
385 // copyBuffer is the actual implementation of Copy and CopyBuffer.
386 // if buf is nil, one is allocated.
387 func copyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error) {
388 // If the reader has a WriteTo method, use it to do the copy.
389 // Avoids an allocation and a copy.
390 if wt, ok := src.(WriterTo); ok {
391 return wt.WriteTo(dst)
392 }
393 // Similarly, if the writer has a ReadFrom method, use it to do the copy.
394 if rt, ok := dst.(ReaderFrom); ok {
395 return rt.ReadFrom(src)
396 }
```
From readahead package
```
// WriteTo writes data to w until there's no more data to write or when an error occurs.
// The return value n is the number of bytes written.
// Any error encountered during the write is also returned.
func (a *reader) WriteTo(w io.Writer) (n int64, err error) {
if a.err != nil {
return 0, a.err
}
n = 0
for {
err = a.fill()
if err != nil {
return n, err
}
n2, err := w.Write(a.cur.buffer())
a.cur.inc(n2)
n += int64(n2)
if err != nil {
return n, err
}
```
2021-01-06 12:36:55 -05:00
|
|
|
if _, err = fsCreateFile(GlobalContext, pathJoin(path, "success-vol", "path/to/success-file"), reader, 0); err != nil {
|
2017-01-16 20:05:00 -05:00
|
|
|
t.Fatalf("Unable to create file, %s", err)
|
|
|
|
}
|
|
|
|
// Seek back.
|
|
|
|
reader.Seek(0, 0)
|
|
|
|
|
|
|
|
testCases := []struct {
|
|
|
|
srcFSPath string
|
|
|
|
srcVol string
|
|
|
|
srcPath string
|
|
|
|
expectedErr error
|
|
|
|
}{
|
|
|
|
// Test case - 1.
|
|
|
|
// Test case with valid inputs, expected to pass.
|
|
|
|
{
|
|
|
|
srcFSPath: path,
|
|
|
|
srcVol: "success-vol",
|
|
|
|
srcPath: "success-file",
|
|
|
|
expectedErr: nil,
|
|
|
|
},
|
|
|
|
// Test case - 2.
|
|
|
|
// Test case with valid inputs, expected to pass.
|
|
|
|
{
|
|
|
|
srcFSPath: path,
|
|
|
|
srcVol: "success-vol",
|
|
|
|
srcPath: "path/to/success-file",
|
|
|
|
expectedErr: nil,
|
|
|
|
},
|
|
|
|
// Test case - 3.
|
|
|
|
// Test case with non-existent file.
|
|
|
|
{
|
|
|
|
srcFSPath: path,
|
|
|
|
srcVol: "success-vol",
|
|
|
|
srcPath: "nonexistent-file",
|
|
|
|
expectedErr: errFileNotFound,
|
|
|
|
},
|
|
|
|
// Test case - 4.
|
|
|
|
// Test case with non-existent file path.
|
|
|
|
{
|
|
|
|
srcFSPath: path,
|
|
|
|
srcVol: "success-vol",
|
|
|
|
srcPath: "path/2/success-file",
|
|
|
|
expectedErr: errFileNotFound,
|
|
|
|
},
|
|
|
|
// Test case - 5.
|
|
|
|
// Test case with path being a directory.
|
|
|
|
{
|
|
|
|
srcFSPath: path,
|
|
|
|
srcVol: "success-vol",
|
|
|
|
srcPath: "path",
|
2018-06-25 21:24:00 -04:00
|
|
|
expectedErr: errFileNotFound,
|
2017-01-16 20:05:00 -05:00
|
|
|
},
|
|
|
|
// Test case - 6.
|
|
|
|
// Test case with src path segment > 255.
|
|
|
|
{
|
|
|
|
srcFSPath: path,
|
|
|
|
srcVol: "success-vol",
|
|
|
|
srcPath: "my-obj-del-0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001",
|
|
|
|
expectedErr: errFileNameTooLong,
|
|
|
|
},
|
|
|
|
// Test case - 7.
|
|
|
|
// Test case validate only srcVol exists.
|
|
|
|
{
|
|
|
|
srcFSPath: path,
|
|
|
|
srcVol: "success-vol",
|
|
|
|
expectedErr: nil,
|
|
|
|
},
|
|
|
|
// Test case - 8.
|
|
|
|
// Test case validate only srcVol doesn't exist.
|
|
|
|
{
|
|
|
|
srcFSPath: path,
|
|
|
|
srcVol: "success-vol-non-existent",
|
|
|
|
expectedErr: errVolumeNotFound,
|
|
|
|
},
|
|
|
|
// Test case - 9.
|
|
|
|
// Test case validate invalid argument.
|
|
|
|
{
|
|
|
|
expectedErr: errInvalidArgument,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, testCase := range testCases {
|
|
|
|
if testCase.srcPath != "" {
|
2020-04-09 12:30:02 -04:00
|
|
|
if _, err := fsStatFile(GlobalContext, pathJoin(testCase.srcFSPath, testCase.srcVol,
|
2018-04-10 12:36:37 -04:00
|
|
|
testCase.srcPath)); err != testCase.expectedErr {
|
2020-06-12 23:04:01 -04:00
|
|
|
t.Fatalf("TestErasureStorage case %d: Expected: \"%s\", got: \"%s\"", i+1, testCase.expectedErr, err)
|
2017-01-16 20:05:00 -05:00
|
|
|
}
|
|
|
|
} else {
|
2020-04-09 12:30:02 -04:00
|
|
|
if _, err := fsStatVolume(GlobalContext, pathJoin(testCase.srcFSPath, testCase.srcVol)); err != testCase.expectedErr {
|
2020-06-12 23:04:01 -04:00
|
|
|
t.Fatalf("TestFS case %d: Expected: \"%s\", got: \"%s\"", i+1, testCase.expectedErr, err)
|
2017-01-16 20:05:00 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestFSCreateAndOpen(t *testing.T) {
|
|
|
|
// Setup test environment.
|
2020-06-12 23:04:01 -04:00
|
|
|
_, path, err := newXLStorageTestSetup()
|
2017-01-16 20:05:00 -05:00
|
|
|
if err != nil {
|
2020-06-12 23:04:01 -04:00
|
|
|
t.Fatalf("Unable to create xlStorage test setup, %s", err)
|
2017-01-16 20:05:00 -05:00
|
|
|
}
|
2017-08-12 22:25:43 -04:00
|
|
|
defer os.RemoveAll(path)
|
2017-01-16 20:05:00 -05:00
|
|
|
|
2020-04-09 12:30:02 -04:00
|
|
|
if err = fsMkdir(GlobalContext, pathJoin(path, "success-vol")); err != nil {
|
2017-01-16 20:05:00 -05:00
|
|
|
t.Fatalf("Unable to create directory, %s", err)
|
|
|
|
}
|
|
|
|
|
fix: use buffers only when necessary for io.Copy() (#11229)
Use separate sync.Pool for writes/reads
Avoid passing buffers for io.CopyBuffer()
if the writer or reader implement io.WriteTo or io.ReadFrom
respectively then its useless for sync.Pool to allocate
buffers on its own since that will be completely ignored
by the io.CopyBuffer Go implementation.
Improve this wherever we see this to be optimal.
This allows us to be more efficient on memory usage.
```
385 // copyBuffer is the actual implementation of Copy and CopyBuffer.
386 // if buf is nil, one is allocated.
387 func copyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error) {
388 // If the reader has a WriteTo method, use it to do the copy.
389 // Avoids an allocation and a copy.
390 if wt, ok := src.(WriterTo); ok {
391 return wt.WriteTo(dst)
392 }
393 // Similarly, if the writer has a ReadFrom method, use it to do the copy.
394 if rt, ok := dst.(ReaderFrom); ok {
395 return rt.ReadFrom(src)
396 }
```
From readahead package
```
// WriteTo writes data to w until there's no more data to write or when an error occurs.
// The return value n is the number of bytes written.
// Any error encountered during the write is also returned.
func (a *reader) WriteTo(w io.Writer) (n int64, err error) {
if a.err != nil {
return 0, a.err
}
n = 0
for {
err = a.fill()
if err != nil {
return n, err
}
n2, err := w.Write(a.cur.buffer())
a.cur.inc(n2)
n += int64(n2)
if err != nil {
return n, err
}
```
2021-01-06 12:36:55 -05:00
|
|
|
if _, err = fsCreateFile(GlobalContext, "", nil, 0); err != errInvalidArgument {
|
2017-01-16 20:05:00 -05:00
|
|
|
t.Fatal("Unexpected error", err)
|
|
|
|
}
|
|
|
|
|
2020-04-09 12:30:02 -04:00
|
|
|
if _, _, err = fsOpenFile(GlobalContext, "", -1); err != errInvalidArgument {
|
2017-01-16 20:05:00 -05:00
|
|
|
t.Fatal("Unexpected error", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
var reader = bytes.NewReader([]byte("Hello, world"))
|
fix: use buffers only when necessary for io.Copy() (#11229)
Use separate sync.Pool for writes/reads
Avoid passing buffers for io.CopyBuffer()
if the writer or reader implement io.WriteTo or io.ReadFrom
respectively then its useless for sync.Pool to allocate
buffers on its own since that will be completely ignored
by the io.CopyBuffer Go implementation.
Improve this wherever we see this to be optimal.
This allows us to be more efficient on memory usage.
```
385 // copyBuffer is the actual implementation of Copy and CopyBuffer.
386 // if buf is nil, one is allocated.
387 func copyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error) {
388 // If the reader has a WriteTo method, use it to do the copy.
389 // Avoids an allocation and a copy.
390 if wt, ok := src.(WriterTo); ok {
391 return wt.WriteTo(dst)
392 }
393 // Similarly, if the writer has a ReadFrom method, use it to do the copy.
394 if rt, ok := dst.(ReaderFrom); ok {
395 return rt.ReadFrom(src)
396 }
```
From readahead package
```
// WriteTo writes data to w until there's no more data to write or when an error occurs.
// The return value n is the number of bytes written.
// Any error encountered during the write is also returned.
func (a *reader) WriteTo(w io.Writer) (n int64, err error) {
if a.err != nil {
return 0, a.err
}
n = 0
for {
err = a.fill()
if err != nil {
return n, err
}
n2, err := w.Write(a.cur.buffer())
a.cur.inc(n2)
n += int64(n2)
if err != nil {
return n, err
}
```
2021-01-06 12:36:55 -05:00
|
|
|
if _, err = fsCreateFile(GlobalContext, pathJoin(path, "success-vol", "success-file"), reader, 0); err != nil {
|
2017-01-16 20:05:00 -05:00
|
|
|
t.Fatalf("Unable to create file, %s", err)
|
|
|
|
}
|
|
|
|
// Seek back.
|
|
|
|
reader.Seek(0, 0)
|
|
|
|
|
|
|
|
testCases := []struct {
|
|
|
|
srcVol string
|
|
|
|
srcPath string
|
|
|
|
expectedErr error
|
|
|
|
}{
|
|
|
|
// Test case - 1.
|
|
|
|
// Test case with segment of the volume name > 255.
|
|
|
|
{
|
|
|
|
srcVol: "my-obj-del-0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001",
|
|
|
|
srcPath: "success-file",
|
|
|
|
expectedErr: errFileNameTooLong,
|
|
|
|
},
|
|
|
|
// Test case - 2.
|
|
|
|
// Test case with src path segment > 255.
|
|
|
|
{
|
|
|
|
srcVol: "success-vol",
|
|
|
|
srcPath: "my-obj-del-0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001",
|
|
|
|
expectedErr: errFileNameTooLong,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, testCase := range testCases {
|
fix: use buffers only when necessary for io.Copy() (#11229)
Use separate sync.Pool for writes/reads
Avoid passing buffers for io.CopyBuffer()
if the writer or reader implement io.WriteTo or io.ReadFrom
respectively then its useless for sync.Pool to allocate
buffers on its own since that will be completely ignored
by the io.CopyBuffer Go implementation.
Improve this wherever we see this to be optimal.
This allows us to be more efficient on memory usage.
```
385 // copyBuffer is the actual implementation of Copy and CopyBuffer.
386 // if buf is nil, one is allocated.
387 func copyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error) {
388 // If the reader has a WriteTo method, use it to do the copy.
389 // Avoids an allocation and a copy.
390 if wt, ok := src.(WriterTo); ok {
391 return wt.WriteTo(dst)
392 }
393 // Similarly, if the writer has a ReadFrom method, use it to do the copy.
394 if rt, ok := dst.(ReaderFrom); ok {
395 return rt.ReadFrom(src)
396 }
```
From readahead package
```
// WriteTo writes data to w until there's no more data to write or when an error occurs.
// The return value n is the number of bytes written.
// Any error encountered during the write is also returned.
func (a *reader) WriteTo(w io.Writer) (n int64, err error) {
if a.err != nil {
return 0, a.err
}
n = 0
for {
err = a.fill()
if err != nil {
return n, err
}
n2, err := w.Write(a.cur.buffer())
a.cur.inc(n2)
n += int64(n2)
if err != nil {
return n, err
}
```
2021-01-06 12:36:55 -05:00
|
|
|
_, err = fsCreateFile(GlobalContext, pathJoin(path, testCase.srcVol, testCase.srcPath), reader, 0)
|
2018-04-10 12:36:37 -04:00
|
|
|
if err != testCase.expectedErr {
|
2017-01-16 20:05:00 -05:00
|
|
|
t.Errorf("Test case %d: Expected: \"%s\", got: \"%s\"", i+1, testCase.expectedErr, err)
|
|
|
|
}
|
2020-04-09 12:30:02 -04:00
|
|
|
_, _, err = fsOpenFile(GlobalContext, pathJoin(path, testCase.srcVol, testCase.srcPath), 0)
|
2018-04-10 12:36:37 -04:00
|
|
|
if err != testCase.expectedErr {
|
2017-01-16 20:05:00 -05:00
|
|
|
t.Errorf("Test case %d: Expected: \"%s\", got: \"%s\"", i+1, testCase.expectedErr, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Attempt to open a directory.
|
2020-04-09 12:30:02 -04:00
|
|
|
if _, _, err = fsOpenFile(GlobalContext, pathJoin(path), 0); err != errIsNotRegular {
|
2017-01-16 20:05:00 -05:00
|
|
|
t.Fatal("Unexpected error", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestFSDeletes(t *testing.T) {
|
2020-06-12 23:04:01 -04:00
|
|
|
// create xlStorage test setup
|
|
|
|
_, path, err := newXLStorageTestSetup()
|
2017-01-16 20:05:00 -05:00
|
|
|
if err != nil {
|
2020-06-12 23:04:01 -04:00
|
|
|
t.Fatalf("Unable to create xlStorage test setup, %s", err)
|
2017-01-16 20:05:00 -05:00
|
|
|
}
|
2017-08-12 22:25:43 -04:00
|
|
|
defer os.RemoveAll(path)
|
2017-01-16 20:05:00 -05:00
|
|
|
|
|
|
|
// Setup test environment.
|
2020-04-09 12:30:02 -04:00
|
|
|
if err = fsMkdir(GlobalContext, pathJoin(path, "success-vol")); err != nil {
|
2017-01-16 20:05:00 -05:00
|
|
|
t.Fatalf("Unable to create directory, %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
var reader = bytes.NewReader([]byte("Hello, world"))
|
fix: use buffers only when necessary for io.Copy() (#11229)
Use separate sync.Pool for writes/reads
Avoid passing buffers for io.CopyBuffer()
if the writer or reader implement io.WriteTo or io.ReadFrom
respectively then its useless for sync.Pool to allocate
buffers on its own since that will be completely ignored
by the io.CopyBuffer Go implementation.
Improve this wherever we see this to be optimal.
This allows us to be more efficient on memory usage.
```
385 // copyBuffer is the actual implementation of Copy and CopyBuffer.
386 // if buf is nil, one is allocated.
387 func copyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error) {
388 // If the reader has a WriteTo method, use it to do the copy.
389 // Avoids an allocation and a copy.
390 if wt, ok := src.(WriterTo); ok {
391 return wt.WriteTo(dst)
392 }
393 // Similarly, if the writer has a ReadFrom method, use it to do the copy.
394 if rt, ok := dst.(ReaderFrom); ok {
395 return rt.ReadFrom(src)
396 }
```
From readahead package
```
// WriteTo writes data to w until there's no more data to write or when an error occurs.
// The return value n is the number of bytes written.
// Any error encountered during the write is also returned.
func (a *reader) WriteTo(w io.Writer) (n int64, err error) {
if a.err != nil {
return 0, a.err
}
n = 0
for {
err = a.fill()
if err != nil {
return n, err
}
n2, err := w.Write(a.cur.buffer())
a.cur.inc(n2)
n += int64(n2)
if err != nil {
return n, err
}
```
2021-01-06 12:36:55 -05:00
|
|
|
if _, err = fsCreateFile(GlobalContext, pathJoin(path, "success-vol", "success-file"), reader, reader.Size()); err != nil {
|
2017-01-16 20:05:00 -05:00
|
|
|
t.Fatalf("Unable to create file, %s", err)
|
|
|
|
}
|
|
|
|
// Seek back.
|
2017-08-03 23:04:28 -04:00
|
|
|
reader.Seek(0, io.SeekStart)
|
|
|
|
|
|
|
|
// folder is not empty
|
2020-04-09 12:30:02 -04:00
|
|
|
err = fsMkdir(GlobalContext, pathJoin(path, "success-vol", "not-empty"))
|
2017-08-03 23:04:28 -04:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
err = ioutil.WriteFile(pathJoin(path, "success-vol", "not-empty", "file"), []byte("data"), 0777)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// recursive
|
2020-04-09 12:30:02 -04:00
|
|
|
if err = fsMkdir(GlobalContext, pathJoin(path, "success-vol", "parent")); err != nil {
|
2017-08-03 23:04:28 -04:00
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2020-04-09 12:30:02 -04:00
|
|
|
if err = fsMkdir(GlobalContext, pathJoin(path, "success-vol", "parent", "dir")); err != nil {
|
2017-08-03 23:04:28 -04:00
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2017-01-16 20:05:00 -05:00
|
|
|
|
|
|
|
testCases := []struct {
|
2017-08-03 23:04:28 -04:00
|
|
|
basePath string
|
2017-01-16 20:05:00 -05:00
|
|
|
srcVol string
|
|
|
|
srcPath string
|
|
|
|
expectedErr error
|
|
|
|
}{
|
|
|
|
// valid case with existing volume and file to delete.
|
|
|
|
{
|
2017-08-03 23:04:28 -04:00
|
|
|
basePath: path,
|
2017-01-16 20:05:00 -05:00
|
|
|
srcVol: "success-vol",
|
|
|
|
srcPath: "success-file",
|
|
|
|
expectedErr: nil,
|
|
|
|
},
|
2020-10-28 12:18:35 -04:00
|
|
|
// The file was deleted in the last case, so Delete should fail.
|
2017-01-16 20:05:00 -05:00
|
|
|
{
|
2017-08-03 23:04:28 -04:00
|
|
|
basePath: path,
|
2017-01-16 20:05:00 -05:00
|
|
|
srcVol: "success-vol",
|
|
|
|
srcPath: "success-file",
|
|
|
|
expectedErr: errFileNotFound,
|
|
|
|
},
|
|
|
|
// Test case with segment of the volume name > 255.
|
|
|
|
{
|
2017-08-03 23:04:28 -04:00
|
|
|
basePath: path,
|
2017-01-16 20:05:00 -05:00
|
|
|
srcVol: "my-obj-del-0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001",
|
|
|
|
srcPath: "success-file",
|
|
|
|
expectedErr: errFileNameTooLong,
|
|
|
|
},
|
|
|
|
// Test case with src path segment > 255.
|
|
|
|
{
|
2017-08-03 23:04:28 -04:00
|
|
|
basePath: path,
|
2017-01-16 20:05:00 -05:00
|
|
|
srcVol: "success-vol",
|
|
|
|
srcPath: "my-obj-del-0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001",
|
|
|
|
expectedErr: errFileNameTooLong,
|
|
|
|
},
|
2017-08-03 23:04:28 -04:00
|
|
|
// Base path is way too long.
|
|
|
|
{
|
|
|
|
basePath: "path03333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333",
|
|
|
|
srcVol: "success-vol",
|
|
|
|
srcPath: "object",
|
|
|
|
expectedErr: errFileNameTooLong,
|
|
|
|
},
|
|
|
|
// Directory is not empty. Should give nil, but won't delete.
|
|
|
|
{
|
|
|
|
basePath: path,
|
|
|
|
srcVol: "success-vol",
|
|
|
|
srcPath: "not-empty",
|
|
|
|
expectedErr: nil,
|
|
|
|
},
|
|
|
|
// Should delete recursively.
|
|
|
|
{
|
|
|
|
basePath: path,
|
|
|
|
srcVol: "success-vol",
|
|
|
|
srcPath: pathJoin("parent", "dir"),
|
|
|
|
expectedErr: nil,
|
|
|
|
},
|
2017-01-16 20:05:00 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
for i, testCase := range testCases {
|
2020-04-09 12:30:02 -04:00
|
|
|
if err = fsDeleteFile(GlobalContext, testCase.basePath, pathJoin(testCase.basePath, testCase.srcVol, testCase.srcPath)); err != testCase.expectedErr {
|
2017-01-16 20:05:00 -05:00
|
|
|
t.Errorf("Test case %d: Expected: \"%s\", got: \"%s\"", i+1, testCase.expectedErr, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-03 23:04:28 -04:00
|
|
|
func BenchmarkFSDeleteFile(b *testing.B) {
|
2020-06-12 23:04:01 -04:00
|
|
|
// create xlStorage test setup
|
|
|
|
_, path, err := newXLStorageTestSetup()
|
2017-08-03 23:04:28 -04:00
|
|
|
if err != nil {
|
2020-06-12 23:04:01 -04:00
|
|
|
b.Fatalf("Unable to create xlStorage test setup, %s", err)
|
2017-08-03 23:04:28 -04:00
|
|
|
}
|
2017-08-12 22:25:43 -04:00
|
|
|
defer os.RemoveAll(path)
|
2017-08-03 23:04:28 -04:00
|
|
|
|
|
|
|
// Setup test environment.
|
2020-04-09 12:30:02 -04:00
|
|
|
if err = fsMkdir(GlobalContext, pathJoin(path, "benchmark")); err != nil {
|
2017-08-03 23:04:28 -04:00
|
|
|
b.Fatalf("Unable to create directory, %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
benchDir := pathJoin(path, "benchmark")
|
|
|
|
filename := pathJoin(benchDir, "file.txt")
|
|
|
|
|
|
|
|
b.ResetTimer()
|
|
|
|
// We need to create and delete the file sequentially inside the benchmark.
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
|
|
b.StopTimer()
|
|
|
|
err = ioutil.WriteFile(filename, []byte("data"), 0777)
|
|
|
|
if err != nil {
|
|
|
|
b.Fatal(err)
|
|
|
|
}
|
|
|
|
b.StartTimer()
|
|
|
|
|
2020-04-09 12:30:02 -04:00
|
|
|
err = fsDeleteFile(GlobalContext, benchDir, filename)
|
2017-08-03 23:04:28 -04:00
|
|
|
if err != nil {
|
|
|
|
b.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-16 20:05:00 -05:00
|
|
|
// Tests fs removes.
|
|
|
|
func TestFSRemoves(t *testing.T) {
|
2020-06-12 23:04:01 -04:00
|
|
|
// create xlStorage test setup
|
|
|
|
_, path, err := newXLStorageTestSetup()
|
2017-01-16 20:05:00 -05:00
|
|
|
if err != nil {
|
2020-06-12 23:04:01 -04:00
|
|
|
t.Fatalf("Unable to create xlStorage test setup, %s", err)
|
2017-01-16 20:05:00 -05:00
|
|
|
}
|
2017-08-12 22:25:43 -04:00
|
|
|
defer os.RemoveAll(path)
|
2017-01-16 20:05:00 -05:00
|
|
|
|
|
|
|
// Setup test environment.
|
2020-04-09 12:30:02 -04:00
|
|
|
if err = fsMkdir(GlobalContext, pathJoin(path, "success-vol")); err != nil {
|
2017-01-16 20:05:00 -05:00
|
|
|
t.Fatalf("Unable to create directory, %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
var reader = bytes.NewReader([]byte("Hello, world"))
|
fix: use buffers only when necessary for io.Copy() (#11229)
Use separate sync.Pool for writes/reads
Avoid passing buffers for io.CopyBuffer()
if the writer or reader implement io.WriteTo or io.ReadFrom
respectively then its useless for sync.Pool to allocate
buffers on its own since that will be completely ignored
by the io.CopyBuffer Go implementation.
Improve this wherever we see this to be optimal.
This allows us to be more efficient on memory usage.
```
385 // copyBuffer is the actual implementation of Copy and CopyBuffer.
386 // if buf is nil, one is allocated.
387 func copyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error) {
388 // If the reader has a WriteTo method, use it to do the copy.
389 // Avoids an allocation and a copy.
390 if wt, ok := src.(WriterTo); ok {
391 return wt.WriteTo(dst)
392 }
393 // Similarly, if the writer has a ReadFrom method, use it to do the copy.
394 if rt, ok := dst.(ReaderFrom); ok {
395 return rt.ReadFrom(src)
396 }
```
From readahead package
```
// WriteTo writes data to w until there's no more data to write or when an error occurs.
// The return value n is the number of bytes written.
// Any error encountered during the write is also returned.
func (a *reader) WriteTo(w io.Writer) (n int64, err error) {
if a.err != nil {
return 0, a.err
}
n = 0
for {
err = a.fill()
if err != nil {
return n, err
}
n2, err := w.Write(a.cur.buffer())
a.cur.inc(n2)
n += int64(n2)
if err != nil {
return n, err
}
```
2021-01-06 12:36:55 -05:00
|
|
|
if _, err = fsCreateFile(GlobalContext, pathJoin(path, "success-vol", "success-file"), reader, 0); err != nil {
|
2017-01-16 20:05:00 -05:00
|
|
|
t.Fatalf("Unable to create file, %s", err)
|
|
|
|
}
|
|
|
|
// Seek back.
|
|
|
|
reader.Seek(0, 0)
|
|
|
|
|
fix: use buffers only when necessary for io.Copy() (#11229)
Use separate sync.Pool for writes/reads
Avoid passing buffers for io.CopyBuffer()
if the writer or reader implement io.WriteTo or io.ReadFrom
respectively then its useless for sync.Pool to allocate
buffers on its own since that will be completely ignored
by the io.CopyBuffer Go implementation.
Improve this wherever we see this to be optimal.
This allows us to be more efficient on memory usage.
```
385 // copyBuffer is the actual implementation of Copy and CopyBuffer.
386 // if buf is nil, one is allocated.
387 func copyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error) {
388 // If the reader has a WriteTo method, use it to do the copy.
389 // Avoids an allocation and a copy.
390 if wt, ok := src.(WriterTo); ok {
391 return wt.WriteTo(dst)
392 }
393 // Similarly, if the writer has a ReadFrom method, use it to do the copy.
394 if rt, ok := dst.(ReaderFrom); ok {
395 return rt.ReadFrom(src)
396 }
```
From readahead package
```
// WriteTo writes data to w until there's no more data to write or when an error occurs.
// The return value n is the number of bytes written.
// Any error encountered during the write is also returned.
func (a *reader) WriteTo(w io.Writer) (n int64, err error) {
if a.err != nil {
return 0, a.err
}
n = 0
for {
err = a.fill()
if err != nil {
return n, err
}
n2, err := w.Write(a.cur.buffer())
a.cur.inc(n2)
n += int64(n2)
if err != nil {
return n, err
}
```
2021-01-06 12:36:55 -05:00
|
|
|
if _, err = fsCreateFile(GlobalContext, pathJoin(path, "success-vol", "success-file-new"), reader, 0); err != nil {
|
2017-01-16 20:05:00 -05:00
|
|
|
t.Fatalf("Unable to create file, %s", err)
|
|
|
|
}
|
|
|
|
// Seek back.
|
|
|
|
reader.Seek(0, 0)
|
|
|
|
|
|
|
|
testCases := []struct {
|
|
|
|
srcFSPath string
|
|
|
|
srcVol string
|
|
|
|
srcPath string
|
|
|
|
expectedErr error
|
|
|
|
}{
|
|
|
|
// Test case - 1.
|
|
|
|
// valid case with existing volume and file to delete.
|
|
|
|
{
|
|
|
|
srcFSPath: path,
|
|
|
|
srcVol: "success-vol",
|
|
|
|
srcPath: "success-file",
|
|
|
|
expectedErr: nil,
|
|
|
|
},
|
|
|
|
// Test case - 2.
|
2020-10-28 12:18:35 -04:00
|
|
|
// The file was deleted in the last case, so Delete should fail.
|
2017-01-16 20:05:00 -05:00
|
|
|
{
|
|
|
|
srcFSPath: path,
|
|
|
|
srcVol: "success-vol",
|
|
|
|
srcPath: "success-file",
|
|
|
|
expectedErr: errFileNotFound,
|
|
|
|
},
|
|
|
|
// Test case - 3.
|
|
|
|
// Test case with segment of the volume name > 255.
|
|
|
|
{
|
|
|
|
srcFSPath: path,
|
|
|
|
srcVol: "my-obj-del-0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001",
|
|
|
|
srcPath: "success-file",
|
|
|
|
expectedErr: errFileNameTooLong,
|
|
|
|
},
|
|
|
|
// Test case - 4.
|
|
|
|
// Test case with src path segment > 255.
|
|
|
|
{
|
|
|
|
srcFSPath: path,
|
|
|
|
srcVol: "success-vol",
|
|
|
|
srcPath: "my-obj-del-0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001",
|
|
|
|
expectedErr: errFileNameTooLong,
|
|
|
|
},
|
|
|
|
// Test case - 5.
|
|
|
|
// Test case with src path empty.
|
|
|
|
{
|
|
|
|
srcFSPath: path,
|
|
|
|
srcVol: "success-vol",
|
|
|
|
expectedErr: errVolumeNotEmpty,
|
|
|
|
},
|
|
|
|
// Test case - 6.
|
|
|
|
// Test case with src path empty.
|
|
|
|
{
|
|
|
|
srcFSPath: path,
|
|
|
|
srcVol: "my-obj-del-0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001",
|
|
|
|
expectedErr: errFileNameTooLong,
|
|
|
|
},
|
|
|
|
// Test case - 7.
|
|
|
|
// Test case with src path empty.
|
|
|
|
{
|
|
|
|
srcFSPath: path,
|
|
|
|
srcVol: "non-existent",
|
|
|
|
expectedErr: errVolumeNotFound,
|
|
|
|
},
|
|
|
|
// Test case - 8.
|
|
|
|
// Test case with src and volume path empty.
|
|
|
|
{
|
|
|
|
expectedErr: errInvalidArgument,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, testCase := range testCases {
|
|
|
|
if testCase.srcPath != "" {
|
2020-04-09 12:30:02 -04:00
|
|
|
if err = fsRemoveFile(GlobalContext, pathJoin(testCase.srcFSPath, testCase.srcVol, testCase.srcPath)); err != testCase.expectedErr {
|
2017-01-16 20:05:00 -05:00
|
|
|
t.Errorf("Test case %d: Expected: \"%s\", got: \"%s\"", i+1, testCase.expectedErr, err)
|
|
|
|
}
|
|
|
|
} else {
|
2020-04-09 12:30:02 -04:00
|
|
|
if err = fsRemoveDir(GlobalContext, pathJoin(testCase.srcFSPath, testCase.srcVol, testCase.srcPath)); err != testCase.expectedErr {
|
2017-01-16 20:05:00 -05:00
|
|
|
t.Error(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-09 12:30:02 -04:00
|
|
|
if err = fsRemoveAll(GlobalContext, pathJoin(path, "success-vol")); err != nil {
|
2017-01-16 20:05:00 -05:00
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
2020-04-09 12:30:02 -04:00
|
|
|
if err = fsRemoveAll(GlobalContext, ""); err != errInvalidArgument {
|
2017-01-16 20:05:00 -05:00
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
2020-04-09 12:30:02 -04:00
|
|
|
if err = fsRemoveAll(GlobalContext, "my-obj-del-0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001"); err != errFileNameTooLong {
|
2017-01-16 20:05:00 -05:00
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
2017-04-14 15:06:24 -04:00
|
|
|
|
|
|
|
func TestFSRemoveMeta(t *testing.T) {
|
2020-06-12 23:04:01 -04:00
|
|
|
// create xlStorage test setup
|
|
|
|
_, fsPath, err := newXLStorageTestSetup()
|
2017-04-14 15:06:24 -04:00
|
|
|
if err != nil {
|
2020-06-12 23:04:01 -04:00
|
|
|
t.Fatalf("Unable to create xlStorage test setup, %s", err)
|
2017-04-14 15:06:24 -04:00
|
|
|
}
|
2017-08-12 22:25:43 -04:00
|
|
|
defer os.RemoveAll(fsPath)
|
2017-04-14 15:06:24 -04:00
|
|
|
|
|
|
|
// Setup test environment.
|
2020-04-09 12:30:02 -04:00
|
|
|
if err = fsMkdir(GlobalContext, pathJoin(fsPath, "success-vol")); err != nil {
|
2017-04-14 15:06:24 -04:00
|
|
|
t.Fatalf("Unable to create directory, %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
filePath := pathJoin(fsPath, "success-vol", "success-file")
|
|
|
|
|
|
|
|
var reader = bytes.NewReader([]byte("Hello, world"))
|
fix: use buffers only when necessary for io.Copy() (#11229)
Use separate sync.Pool for writes/reads
Avoid passing buffers for io.CopyBuffer()
if the writer or reader implement io.WriteTo or io.ReadFrom
respectively then its useless for sync.Pool to allocate
buffers on its own since that will be completely ignored
by the io.CopyBuffer Go implementation.
Improve this wherever we see this to be optimal.
This allows us to be more efficient on memory usage.
```
385 // copyBuffer is the actual implementation of Copy and CopyBuffer.
386 // if buf is nil, one is allocated.
387 func copyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error) {
388 // If the reader has a WriteTo method, use it to do the copy.
389 // Avoids an allocation and a copy.
390 if wt, ok := src.(WriterTo); ok {
391 return wt.WriteTo(dst)
392 }
393 // Similarly, if the writer has a ReadFrom method, use it to do the copy.
394 if rt, ok := dst.(ReaderFrom); ok {
395 return rt.ReadFrom(src)
396 }
```
From readahead package
```
// WriteTo writes data to w until there's no more data to write or when an error occurs.
// The return value n is the number of bytes written.
// Any error encountered during the write is also returned.
func (a *reader) WriteTo(w io.Writer) (n int64, err error) {
if a.err != nil {
return 0, a.err
}
n = 0
for {
err = a.fill()
if err != nil {
return n, err
}
n2, err := w.Write(a.cur.buffer())
a.cur.inc(n2)
n += int64(n2)
if err != nil {
return n, err
}
```
2021-01-06 12:36:55 -05:00
|
|
|
if _, err = fsCreateFile(GlobalContext, filePath, reader, 0); err != nil {
|
2017-04-14 15:06:24 -04:00
|
|
|
t.Fatalf("Unable to create file, %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
rwPool := &fsIOPool{
|
|
|
|
readersMap: make(map[string]*lock.RLockedFile),
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, err := rwPool.Open(filePath); err != nil {
|
|
|
|
t.Fatalf("Unable to lock file %s", filePath)
|
|
|
|
}
|
|
|
|
|
|
|
|
defer rwPool.Close(filePath)
|
|
|
|
|
|
|
|
tmpDir, tmpErr := ioutil.TempDir(globalTestTmpDir, "minio-")
|
|
|
|
if tmpErr != nil {
|
|
|
|
t.Fatal(tmpErr)
|
|
|
|
}
|
|
|
|
|
2020-04-09 12:30:02 -04:00
|
|
|
if err := fsRemoveMeta(GlobalContext, fsPath, filePath, tmpDir); err != nil {
|
2017-04-14 15:06:24 -04:00
|
|
|
t.Fatalf("Unable to remove file, %s", err)
|
|
|
|
}
|
|
|
|
|
2020-11-23 11:36:49 -05:00
|
|
|
if _, err := os.Stat((filePath)); !osIsNotExist(err) {
|
2017-04-14 15:06:24 -04:00
|
|
|
t.Fatalf("`%s` file found though it should have been deleted.", filePath)
|
|
|
|
}
|
|
|
|
|
2020-11-23 11:36:49 -05:00
|
|
|
if _, err := os.Stat((path.Dir(filePath))); !osIsNotExist(err) {
|
2017-04-14 15:06:24 -04:00
|
|
|
t.Fatalf("`%s` parent directory found though it should have been deleted.", filePath)
|
|
|
|
}
|
|
|
|
}
|
2018-05-08 22:09:15 -04:00
|
|
|
|
|
|
|
func TestFSIsFile(t *testing.T) {
|
|
|
|
dirPath, err := ioutil.TempDir(globalTestTmpDir, "minio-")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Unable to create tmp directory %s", err)
|
|
|
|
}
|
|
|
|
defer os.RemoveAll(dirPath)
|
|
|
|
|
|
|
|
filePath := pathJoin(dirPath, "tmpfile")
|
|
|
|
|
|
|
|
if err = ioutil.WriteFile(filePath, nil, 0777); err != nil {
|
|
|
|
t.Fatalf("Unable to create file %s", filePath)
|
|
|
|
}
|
|
|
|
|
2020-04-09 12:30:02 -04:00
|
|
|
if !fsIsFile(GlobalContext, filePath) {
|
2018-05-08 22:09:15 -04:00
|
|
|
t.Fatalf("Expected %s to be a file", filePath)
|
|
|
|
}
|
|
|
|
}
|