diff --git a/cmd/gateway/hdfs/gateway-hdfs.go b/cmd/gateway/hdfs/gateway-hdfs.go index 0f190a7aa..c47dc990c 100644 --- a/cmd/gateway/hdfs/gateway-hdfs.go +++ b/cmd/gateway/hdfs/gateway-hdfs.go @@ -18,6 +18,7 @@ package hdfs import ( "context" + "errors" "fmt" "io" "net" @@ -280,7 +281,7 @@ func hdfsToObjectErr(ctx context.Context, err error, params ...string) error { return minio.PrefixAccessDenied{Bucket: bucket, Object: object} } return minio.BucketAlreadyOwnedByYou{Bucket: bucket} - case isSysErrNotEmpty(err): + case errors.Is(err, syscall.ENOTEMPTY): if object != "" { return minio.PrefixAccessDenied{Bucket: bucket, Object: object} } @@ -387,20 +388,6 @@ func (n *hdfsObjects) ListObjects(ctx context.Context, bucket, prefix, marker, d return minio.ListObjects(ctx, n, bucket, prefix, marker, delimiter, maxKeys, n.listPool, n.listDirFactory(), getObjectInfo, getObjectInfo) } -// Check if the given error corresponds to ENOTEMPTY for unix -// and ERROR_DIR_NOT_EMPTY for windows (directory not empty). -func isSysErrNotEmpty(err error) bool { - if err == syscall.ENOTEMPTY { - return true - } - if pathErr, ok := err.(*os.PathError); ok { - if pathErr.Err == syscall.ENOTEMPTY { - return true - } - } - return false -} - // deleteObject deletes a file path if its empty. If it's successfully deleted, // it will recursively move up the tree, deleting empty parent directories // until it finds one with files in it. Returns nil for a non-empty directory. @@ -411,16 +398,13 @@ func (n *hdfsObjects) deleteObject(basePath, deletePath string) error { // Attempt to remove path. if err := n.clnt.Remove(deletePath); err != nil { - switch { - case err == syscall.ENOTEMPTY: - case isSysErrNotEmpty(err): + if errors.Is(err, syscall.ENOTEMPTY) { // Ignore errors if the directory is not empty. The server relies on // this functionality, and sometimes uses recursion that should not // error on parent directories. return nil - default: - return err } + return err } // Trailing slash is removed when found to ensure diff --git a/cmd/http-tracer.go b/cmd/http-tracer.go index b0a33e007..ef9b89f9f 100644 --- a/cmd/http-tracer.go +++ b/cmd/http-tracer.go @@ -175,7 +175,7 @@ func Trace(f http.HandlerFunc, logBody bool, w http.ResponseWriter, r *http.Requ name := getOpName(runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name()) // Setup a http request body recorder - reqHeaders := cloneHeader(r.Header) + reqHeaders := r.Header.Clone() reqHeaders.Set("Content-Length", strconv.Itoa(int(r.ContentLength))) reqHeaders.Set("Host", r.Host) for _, enc := range r.TransferEncoding { @@ -211,7 +211,7 @@ func Trace(f http.HandlerFunc, logBody bool, w http.ResponseWriter, r *http.Requ rs := trace.ResponseInfo{ Time: time.Now().UTC(), - Headers: cloneHeader(respBodyRecorder.Header()), + Headers: respBodyRecorder.Header().Clone(), StatusCode: respBodyRecorder.statusCode, Body: respBodyRecorder.Body(), } diff --git a/cmd/posix-errors.go b/cmd/posix-errors.go index 7fea4061c..e68cf8bb8 100644 --- a/cmd/posix-errors.go +++ b/cmd/posix-errors.go @@ -17,6 +17,7 @@ package cmd import ( + "errors" "os" "runtime" "syscall" @@ -24,86 +25,54 @@ import ( // Function not implemented error func isSysErrNoSys(err error) bool { - if err == syscall.ENOSYS { - return true - } - pathErr, ok := err.(*os.PathError) - return ok && pathErr.Err == syscall.ENOSYS - + return errors.Is(err, syscall.ENOSYS) } // Not supported error func isSysErrOpNotSupported(err error) bool { - if err == syscall.EOPNOTSUPP { - return true - } - pathErr, ok := err.(*os.PathError) - return ok && pathErr.Err == syscall.EOPNOTSUPP - + return errors.Is(err, syscall.EOPNOTSUPP) } // No space left on device error func isSysErrNoSpace(err error) bool { - if err == syscall.ENOSPC { - return true - } - pathErr, ok := err.(*os.PathError) - return ok && pathErr.Err == syscall.ENOSPC + return errors.Is(err, syscall.ENOSPC) } // Input/output error func isSysErrIO(err error) bool { - if err == syscall.EIO { - return true - } - pathErr, ok := err.(*os.PathError) - return ok && pathErr.Err == syscall.EIO + return errors.Is(err, syscall.EIO) } // Check if the given error corresponds to EISDIR (is a directory). func isSysErrIsDir(err error) bool { - if err == syscall.EISDIR { - return true - } - pathErr, ok := err.(*os.PathError) - return ok && pathErr.Err == syscall.EISDIR - + return errors.Is(err, syscall.EISDIR) } // Check if the given error corresponds to ENOTDIR (is not a directory). func isSysErrNotDir(err error) bool { - if err == syscall.ENOTDIR { - return true - } - pathErr, ok := err.(*os.PathError) - return ok && pathErr.Err == syscall.ENOTDIR + return errors.Is(err, syscall.ENOTDIR) } // Check if the given error corresponds to the ENAMETOOLONG (name too long). func isSysErrTooLong(err error) bool { - if err == syscall.ENAMETOOLONG { - return true - } - pathErr, ok := err.(*os.PathError) - return ok && pathErr.Err == syscall.ENAMETOOLONG + return errors.Is(err, syscall.ENAMETOOLONG) } // Check if the given error corresponds to ENOTEMPTY for unix // and ERROR_DIR_NOT_EMPTY for windows (directory not empty). func isSysErrNotEmpty(err error) bool { - if err == syscall.ENOTEMPTY { + if errors.Is(err, syscall.ENOTEMPTY) { return true } - if pathErr, ok := err.(*os.PathError); ok { + var pathErr *os.PathError + if errors.As(err, &pathErr) { if runtime.GOOS == globalWindowsOSName { - if errno, _ok := pathErr.Err.(syscall.Errno); _ok && errno == 0x91 { + var errno syscall.Errno + if errors.As(pathErr.Err, &errno) { // ERROR_DIR_NOT_EMPTY - return true + return errno == 0x91 } } - if pathErr.Err == syscall.ENOTEMPTY { - return true - } } return false } @@ -113,10 +82,12 @@ func isSysErrPathNotFound(err error) bool { if runtime.GOOS != globalWindowsOSName { return false } - if pathErr, ok := err.(*os.PathError); ok { - if errno, _ok := pathErr.Err.(syscall.Errno); _ok && errno == 0x03 { + var pathErr *os.PathError + if errors.As(err, &pathErr) { + var errno syscall.Errno + if errors.As(pathErr.Err, &errno) { // ERROR_PATH_NOT_FOUND - return true + return errno == 0x03 } } return false @@ -128,20 +99,22 @@ func isSysErrHandleInvalid(err error) bool { return false } // Check if err contains ERROR_INVALID_HANDLE errno - errno, ok := err.(syscall.Errno) - return ok && errno == 0x6 + var pathErr *os.PathError + if errors.As(err, &pathErr) { + var errno syscall.Errno + if errors.As(pathErr.Err, &errno) { + // ERROR_PATH_NOT_FOUND + return errno == 0x6 + } + } + return false } func isSysErrCrossDevice(err error) bool { - e, ok := err.(*os.LinkError) - return ok && e.Err == syscall.EXDEV + return errors.Is(err, syscall.EXDEV) } // Check if given error corresponds to too many open files func isSysErrTooManyFiles(err error) bool { - if err == syscall.ENFILE || err == syscall.EMFILE { - return true - } - pathErr, ok := err.(*os.PathError) - return ok && (pathErr.Err == syscall.ENFILE || pathErr.Err == syscall.EMFILE) + return errors.Is(err, syscall.ENFILE) || errors.Is(err, syscall.EMFILE) } diff --git a/cmd/posix.go b/cmd/posix.go index dc04d9080..876cd024c 100644 --- a/cmd/posix.go +++ b/cmd/posix.go @@ -19,6 +19,7 @@ package cmd import ( "context" "encoding/hex" + "errors" "io" "io/ioutil" "os" @@ -845,17 +846,11 @@ func (s *posix) ReadAll(volume, path string) (buf []byte, err error) { return nil, errFileNotFound } else if os.IsPermission(err) { return nil, errFileAccessDenied - } else if pathErr, ok := err.(*os.PathError); ok { - switch pathErr.Err { - case syscall.ENOTDIR, syscall.EISDIR: - return nil, errFileNotFound - default: - if isSysErrHandleInvalid(pathErr.Err) { - // This case is special and needs to be handled for windows. - return nil, errFileNotFound - } - } - return nil, pathErr + } else if errors.Is(err, syscall.ENOTDIR) || errors.Is(err, syscall.EISDIR) { + return nil, errFileNotFound + } else if isSysErrHandleInvalid(err) { + // This case is special and needs to be handled for windows. + return nil, errFileNotFound } else if isSysErrIO(err) { return nil, errFaultyDisk } diff --git a/cmd/ui-errors-utils.go b/cmd/ui-errors-utils.go index aea5c5779..a5eeab94b 100644 --- a/cmd/ui-errors-utils.go +++ b/cmd/ui-errors-utils.go @@ -17,9 +17,9 @@ package cmd import ( + "errors" "fmt" "io" - "net" "os" "syscall" ) @@ -80,27 +80,15 @@ func errorToUIErr(err error) uiErr { } // Show a generic message for known golang errors - switch e := err.(type) { - case *net.OpError: - if e.Op == "listen" { - if oe, ok := e.Err.(*os.SyscallError); ok { - if oe.Err == syscall.EADDRINUSE { - return uiErrPortAlreadyInUse(e).Msg("Specified port '" + e.Addr.String() + "' is already in use") - } else if oe.Err == syscall.EACCES { - return uiErrPortAccess(e).Msg("Insufficient permissions to use specified port '" + e.Addr.String() + "'") - } - } - } - case *os.PathError: - if os.IsPermission(e) { - return uiErrNoPermissionsToAccessDirFiles(e).Msg("Insufficient permissions to access path, `" + e.Path + "`") - } - } - - switch err { - case io.ErrUnexpectedEOF: + if errors.Is(err, syscall.EADDRINUSE) { + return uiErrPortAlreadyInUse(err).Msg("Specified port is already in use") + } else if errors.Is(err, syscall.EACCES) { + return uiErrPortAccess(err).Msg("Insufficient permissions to use specified port") + } else if os.IsPermission(err) { + return uiErrNoPermissionsToAccessDirFiles(err).Msg("Insufficient permissions to access path") + } else if errors.Is(err, io.ErrUnexpectedEOF) { return uiErrUnexpectedDataContent(err) - default: + } else { // Failed to identify what type of error this, return a simple UI error return uiErr{msg: err.Error()} } diff --git a/cmd/utils.go b/cmd/utils.go index 207e20822..052d743dc 100644 --- a/cmd/utils.go +++ b/cmd/utils.go @@ -53,25 +53,13 @@ func IsErrIgnored(err error, ignoredErrs ...error) bool { // IsErr returns whether given error is exact error. func IsErr(err error, errs ...error) bool { for _, exactErr := range errs { - if err == exactErr { + if errors.Is(err, exactErr) { return true } } return false } -// make a copy of http.Header -func cloneHeader(h http.Header) http.Header { - h2 := make(http.Header, len(h)) - for k, vv := range h { - vv2 := make([]string, len(vv)) - copy(vv2, vv) - h2[k] = vv2 - - } - return h2 -} - func request2BucketObjectName(r *http.Request) (bucketName, objectName string) { path, err := getResource(r.URL.Path, r.Host, globalDomainNames) if err != nil { @@ -289,7 +277,7 @@ var globalProfiler minioProfiler // dump the request into a string in JSON format. func dumpRequest(r *http.Request) string { - header := cloneHeader(r.Header) + header := r.Header.Clone() header.Set("Host", r.Host) // Replace all '%' to '%%' so that printer format parser // to ignore URL encoded values. diff --git a/cmd/utils_test.go b/cmd/utils_test.go index e5d2a13cd..195e31bad 100644 --- a/cmd/utils_test.go +++ b/cmd/utils_test.go @@ -30,30 +30,6 @@ import ( "testing" ) -// Tests http.Header clone. -func TestCloneHeader(t *testing.T) { - headers := []http.Header{ - { - "Content-Type": {"text/html; charset=UTF-8"}, - "Content-Length": {"0"}, - }, - { - "Content-Length": {"0", "1", "2"}, - }, - { - "Expires": {"-1"}, - "Content-Length": {"0"}, - "Content-Encoding": {"gzip"}, - }, - } - for i, header := range headers { - clonedHeader := cloneHeader(header) - if !reflect.DeepEqual(header, clonedHeader) { - t.Errorf("Test %d failed", i+1) - } - } -} - // Tests maximum object size. func TestMaxObjectSize(t *testing.T) { sizes := []struct { diff --git a/pkg/lock/lock_nix.go b/pkg/lock/lock_nix.go index 3fd581fca..c7c3a1a32 100644 --- a/pkg/lock/lock_nix.go +++ b/pkg/lock/lock_nix.go @@ -19,7 +19,6 @@ package lock import ( - "fmt" "os" "syscall" ) @@ -39,7 +38,11 @@ func lockedOpenFile(path string, flag int, perm os.FileMode, lockType int) (*Loc case syscall.O_RDWR | syscall.O_CREAT: lockType |= syscall.LOCK_EX default: - return nil, fmt.Errorf("Unsupported flag (%d)", flag) + return nil, &os.PathError{ + Op: "open", + Path: path, + Err: syscall.EINVAL, + } } f, err := os.OpenFile(path, flag|syscall.O_SYNC, perm) diff --git a/pkg/lock/lock_solaris.go b/pkg/lock/lock_solaris.go index a25c8bdd6..e20c6fe83 100644 --- a/pkg/lock/lock_solaris.go +++ b/pkg/lock/lock_solaris.go @@ -19,7 +19,6 @@ package lock import ( - "fmt" "os" "syscall" ) @@ -39,7 +38,11 @@ func lockedOpenFile(path string, flag int, perm os.FileMode, rlockType int) (*Lo case syscall.O_RDWR | syscall.O_CREAT: lockType = syscall.F_WRLCK default: - return nil, fmt.Errorf("Unsupported flag (%d)", flag) + return nil, &os.PathError{ + Op: "open", + Path: path, + Err: syscall.EINVAL, + } } var lock = syscall.Flock_t{