mirror of
https://github.com/minio/minio.git
synced 2024-12-24 22:25:54 -05:00
c791de0e1e
dataDir loosely based on maxima is incorrect and does not work in all situations such as disks in the following order - xl.json migration to xl.meta there may be partial xl.json's leftover if some disks are not yet connected when the disk is yet to come up, since xl.json mtime and xl.meta is same the dataDir maxima doesn't work properly leading to quorum issues. - its also possible that XLV1 might be true among the disks available, make sure to keep FileInfo based on common quorum and skip unexpected disks with the older data format. Also, this PR tests upgrade from older to a newer release if the data is readable and matches the checksum. NOTE: this is just initial work we can build on top of this to do further tests.
183 lines
5.3 KiB
Go
183 lines
5.3 KiB
Go
// 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/>.
|
|
|
|
package cmd
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path"
|
|
)
|
|
|
|
// Wrapper functions to os.RemoveAll, which calls reliableRemoveAll
|
|
// this is to ensure that if there is a racy parent directory
|
|
// create in between we can simply retry the operation.
|
|
func removeAll(dirPath string) (err error) {
|
|
if dirPath == "" {
|
|
return errInvalidArgument
|
|
}
|
|
|
|
if err = checkPathLength(dirPath); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err = reliableRemoveAll(dirPath); err != nil {
|
|
switch {
|
|
case isSysErrNotDir(err):
|
|
// File path cannot be verified since one of
|
|
// the parents is a file.
|
|
return errFileAccessDenied
|
|
case isSysErrPathNotFound(err):
|
|
// This is a special case should be handled only for
|
|
// windows, because windows API does not return "not a
|
|
// directory" error message. Handle this specifically
|
|
// here.
|
|
return errFileAccessDenied
|
|
}
|
|
}
|
|
return err
|
|
}
|
|
|
|
// Reliably retries os.RemoveAll if for some reason os.RemoveAll returns
|
|
// syscall.ENOTEMPTY (children has files).
|
|
func reliableRemoveAll(dirPath string) (err error) {
|
|
i := 0
|
|
for {
|
|
// Removes all the directories and files.
|
|
if err = RemoveAll(dirPath); err != nil {
|
|
// Retry only for the first retryable error.
|
|
if isSysErrNotEmpty(err) && i == 0 {
|
|
i++
|
|
continue
|
|
}
|
|
}
|
|
break
|
|
}
|
|
return err
|
|
}
|
|
|
|
// Wrapper functions to os.MkdirAll, which calls reliableMkdirAll
|
|
// this is to ensure that if there is a racy parent directory
|
|
// delete in between we can simply retry the operation.
|
|
func mkdirAll(dirPath string, mode os.FileMode) (err error) {
|
|
if dirPath == "" {
|
|
return errInvalidArgument
|
|
}
|
|
|
|
if err = checkPathLength(dirPath); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err = reliableMkdirAll(dirPath, mode); err != nil {
|
|
// File path cannot be verified since one of the parents is a file.
|
|
if isSysErrNotDir(err) {
|
|
return errFileAccessDenied
|
|
} else if isSysErrPathNotFound(err) {
|
|
// This is a special case should be handled only for
|
|
// windows, because windows API does not return "not a
|
|
// directory" error message. Handle this specifically here.
|
|
return errFileAccessDenied
|
|
}
|
|
return osErrToFileErr(err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Reliably retries os.MkdirAll if for some reason os.MkdirAll returns
|
|
// syscall.ENOENT (parent does not exist).
|
|
func reliableMkdirAll(dirPath string, mode os.FileMode) (err error) {
|
|
i := 0
|
|
for {
|
|
// Creates all the parent directories, with mode 0777 mkdir honors system umask.
|
|
if err = MkdirAll(dirPath, mode); err != nil {
|
|
// Retry only for the first retryable error.
|
|
if osIsNotExist(err) && i == 0 {
|
|
i++
|
|
continue
|
|
}
|
|
}
|
|
break
|
|
}
|
|
return err
|
|
}
|
|
|
|
// Wrapper function to os.Rename, which calls reliableMkdirAll
|
|
// and reliableRenameAll. This is to ensure that if there is a
|
|
// racy parent directory delete in between we can simply retry
|
|
// the operation.
|
|
func renameAll(srcFilePath, dstFilePath string) (err error) {
|
|
if srcFilePath == "" || dstFilePath == "" {
|
|
return errInvalidArgument
|
|
}
|
|
|
|
if err = checkPathLength(srcFilePath); err != nil {
|
|
return err
|
|
}
|
|
if err = checkPathLength(dstFilePath); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err = reliableRename(srcFilePath, dstFilePath); err != nil {
|
|
switch {
|
|
case isSysErrNotDir(err) && !osIsNotExist(err):
|
|
// Windows can have both isSysErrNotDir(err) and osIsNotExist(err) returning
|
|
// true if the source file path contains an non-existent directory. In that case,
|
|
// we want to return errFileNotFound instead, which will honored in subsequent
|
|
// switch cases
|
|
return errFileAccessDenied
|
|
case isSysErrPathNotFound(err):
|
|
// This is a special case should be handled only for
|
|
// windows, because windows API does not return "not a
|
|
// directory" error message. Handle this specifically here.
|
|
return errFileAccessDenied
|
|
case isSysErrCrossDevice(err):
|
|
return fmt.Errorf("%w (%s)->(%s)", errCrossDeviceLink, srcFilePath, dstFilePath)
|
|
case osIsNotExist(err):
|
|
return errFileNotFound
|
|
case osIsExist(err):
|
|
// This is returned only when destination is a directory and we
|
|
// are attempting a rename from file to directory.
|
|
return errIsNotRegular
|
|
default:
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Reliably retries os.RenameAll if for some reason os.RenameAll returns
|
|
// syscall.ENOENT (parent does not exist).
|
|
func reliableRename(srcFilePath, dstFilePath string) (err error) {
|
|
if err = reliableMkdirAll(path.Dir(dstFilePath), 0777); err != nil {
|
|
return err
|
|
}
|
|
i := 0
|
|
for {
|
|
// After a successful parent directory create attempt a renameAll.
|
|
if err = Rename(srcFilePath, dstFilePath); err != nil {
|
|
// Retry only for the first retryable error.
|
|
if osIsNotExist(err) && i == 0 {
|
|
i++
|
|
continue
|
|
}
|
|
}
|
|
break
|
|
}
|
|
return err
|
|
}
|