mirror of
https://github.com/minio/minio.git
synced 2025-01-12 07:23:23 -05:00
fix: reduce an extra system call for writes instead fail later (#10187)
This commit is contained in:
parent
d90ab904e7
commit
019fe69a57
@ -285,12 +285,18 @@ func fsCreateFile(ctx context.Context, filePath string, reader io.Reader, buf []
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := mkdirAll(pathutil.Dir(filePath), 0777); err != nil {
|
if err := mkdirAll(pathutil.Dir(filePath), 0777); err != nil {
|
||||||
logger.LogIf(ctx, err)
|
switch {
|
||||||
return 0, err
|
case os.IsPermission(err):
|
||||||
}
|
return 0, errFileAccessDenied
|
||||||
|
case os.IsExist(err):
|
||||||
if err := checkDiskFree(pathutil.Dir(filePath), fallocSize); err != nil {
|
return 0, errFileAccessDenied
|
||||||
logger.LogIf(ctx, err)
|
case isSysErrIO(err):
|
||||||
|
return 0, errFaultyDisk
|
||||||
|
case isSysErrInvalidArg(err):
|
||||||
|
return 0, errUnsupportedDisk
|
||||||
|
case isSysErrNoSpace(err):
|
||||||
|
return 0, errDiskFull
|
||||||
|
}
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,7 +58,6 @@ const (
|
|||||||
globalMinioDefaultOwnerID = "02d6176db174dc93cb1b899f7c6078f08654445fe8cf1b6ce98d8855f66bdbf4"
|
globalMinioDefaultOwnerID = "02d6176db174dc93cb1b899f7c6078f08654445fe8cf1b6ce98d8855f66bdbf4"
|
||||||
globalMinioDefaultStorageClass = "STANDARD"
|
globalMinioDefaultStorageClass = "STANDARD"
|
||||||
globalWindowsOSName = "windows"
|
globalWindowsOSName = "windows"
|
||||||
globalNetBSDOSName = "netbsd"
|
|
||||||
globalMacOSName = "darwin"
|
globalMacOSName = "darwin"
|
||||||
globalMinioModeFS = "mode-server-fs"
|
globalMinioModeFS = "mode-server-fs"
|
||||||
globalMinioModeErasure = "mode-server-xl"
|
globalMinioModeErasure = "mode-server-xl"
|
||||||
|
@ -142,5 +142,14 @@ func osErrToFileErr(err error) error {
|
|||||||
if isSysErrHandleInvalid(err) {
|
if isSysErrHandleInvalid(err) {
|
||||||
return errFileNotFound
|
return errFileNotFound
|
||||||
}
|
}
|
||||||
|
if isSysErrIO(err) {
|
||||||
|
return errFaultyDisk
|
||||||
|
}
|
||||||
|
if isSysErrInvalidArg(err) {
|
||||||
|
return errUnsupportedDisk
|
||||||
|
}
|
||||||
|
if isSysErrNoSpace(err) {
|
||||||
|
return errDiskFull
|
||||||
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -48,8 +48,7 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
nullVersionID = "null"
|
nullVersionID = "null"
|
||||||
diskMinFreeSpace = 900 * humanize.MiByte // Min 900MiB free space.
|
diskMinTotalSpace = 900 * humanize.MiByte // Min 900MiB total space.
|
||||||
diskMinTotalSpace = diskMinFreeSpace // Min 900MiB total space.
|
|
||||||
readBlockSize = 4 * humanize.MiByte // Default read block size 4MiB.
|
readBlockSize = 4 * humanize.MiByte // Default read block size 4MiB.
|
||||||
|
|
||||||
// On regular files bigger than this;
|
// On regular files bigger than this;
|
||||||
@ -175,6 +174,7 @@ func getValidPath(path string, requireDirectIO bool) (string, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return path, err
|
return path, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = checkDiskMinTotal(di); err != nil {
|
if err = checkDiskMinTotal(di); err != nil {
|
||||||
return path, err
|
return path, err
|
||||||
}
|
}
|
||||||
@ -276,13 +276,6 @@ func getDiskInfo(diskPath string) (di disk.Info, err error) {
|
|||||||
return di, err
|
return di, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// List of operating systems where we ignore disk space
|
|
||||||
// verification.
|
|
||||||
var ignoreDiskFreeOS = []string{
|
|
||||||
globalWindowsOSName,
|
|
||||||
globalNetBSDOSName,
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if disk total has minimum required size.
|
// check if disk total has minimum required size.
|
||||||
func checkDiskMinTotal(di disk.Info) (err error) {
|
func checkDiskMinTotal(di disk.Info) (err error) {
|
||||||
// Remove 5% from total space for cumulative disk space
|
// Remove 5% from total space for cumulative disk space
|
||||||
@ -294,46 +287,6 @@ func checkDiskMinTotal(di disk.Info) (err error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if disk free has minimum required size.
|
|
||||||
func checkDiskMinFree(di disk.Info) error {
|
|
||||||
// Remove 5% from free space for cumulative disk space used for journalling, inodes etc.
|
|
||||||
availableDiskSpace := float64(di.Free) * diskFillFraction
|
|
||||||
if int64(availableDiskSpace) <= diskMinFreeSpace {
|
|
||||||
return errDiskFull
|
|
||||||
}
|
|
||||||
|
|
||||||
// Success.
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// checkDiskFree verifies if disk path has sufficient minimum free disk space and files.
|
|
||||||
func checkDiskFree(diskPath string, neededSpace int64) (err error) {
|
|
||||||
// We don't validate disk space or inode utilization on windows.
|
|
||||||
// Each windows call to 'GetVolumeInformationW' takes around
|
|
||||||
// 3-5seconds. And StatDISK is not supported by Go for solaris
|
|
||||||
// and netbsd.
|
|
||||||
if contains(ignoreDiskFreeOS, runtime.GOOS) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var di disk.Info
|
|
||||||
di, err = getDiskInfo(diskPath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = checkDiskMinFree(di); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if we have enough space to store data
|
|
||||||
if neededSpace > int64(float64(di.Free)*diskFillFraction) {
|
|
||||||
return errDiskFull
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implements stringer compatible interface.
|
// Implements stringer compatible interface.
|
||||||
func (s *xlStorage) String() string {
|
func (s *xlStorage) String() string {
|
||||||
return s.diskPath
|
return s.diskPath
|
||||||
@ -1643,14 +1596,6 @@ func (s *xlStorage) CreateFile(volume, path string, fileSize int64, r io.Reader)
|
|||||||
atomic.AddInt32(&s.activeIOCount, -1)
|
atomic.AddInt32(&s.activeIOCount, -1)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Validate if disk is indeed free.
|
|
||||||
if err = checkDiskFree(s.diskPath, fileSize); err != nil {
|
|
||||||
if isSysErrIO(err) {
|
|
||||||
return errFaultyDisk
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
volumeDir, err := s.getVolDir(volume)
|
volumeDir, err := s.getVolDir(volume)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -1674,8 +1619,17 @@ func (s *xlStorage) CreateFile(volume, path string, fileSize int64, r io.Reader)
|
|||||||
// Create top level directories if they don't exist.
|
// Create top level directories if they don't exist.
|
||||||
// with mode 0777 mkdir honors system umask.
|
// with mode 0777 mkdir honors system umask.
|
||||||
if err = mkdirAll(slashpath.Dir(filePath), 0777); err != nil {
|
if err = mkdirAll(slashpath.Dir(filePath), 0777); err != nil {
|
||||||
if errors.Is(err, &os.PathError{}) {
|
switch {
|
||||||
|
case os.IsPermission(err):
|
||||||
return errFileAccessDenied
|
return errFileAccessDenied
|
||||||
|
case os.IsExist(err):
|
||||||
|
return errFileAccessDenied
|
||||||
|
case isSysErrIO(err):
|
||||||
|
return errFaultyDisk
|
||||||
|
case isSysErrInvalidArg(err):
|
||||||
|
return errUnsupportedDisk
|
||||||
|
case isSysErrNoSpace(err):
|
||||||
|
return errDiskFull
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -1691,6 +1645,8 @@ func (s *xlStorage) CreateFile(volume, path string, fileSize int64, r io.Reader)
|
|||||||
return errFaultyDisk
|
return errFaultyDisk
|
||||||
case isSysErrInvalidArg(err):
|
case isSysErrInvalidArg(err):
|
||||||
return errUnsupportedDisk
|
return errUnsupportedDisk
|
||||||
|
case isSysErrNoSpace(err):
|
||||||
|
return errDiskFull
|
||||||
default:
|
default:
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -1766,55 +1766,3 @@ func TestCheckDiskTotalMin(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks for restrictions for min free disk space and inodes.
|
|
||||||
func TestCheckDiskFreeMin(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
diskInfo disk.Info
|
|
||||||
err error
|
|
||||||
}{
|
|
||||||
// Test 1 - when fstype is nfs.
|
|
||||||
{
|
|
||||||
diskInfo: disk.Info{
|
|
||||||
Free: diskMinTotalSpace * 3,
|
|
||||||
FSType: "NFS",
|
|
||||||
},
|
|
||||||
err: nil,
|
|
||||||
},
|
|
||||||
// Test 2 - when fstype is xfs and total inodes are less than 10k.
|
|
||||||
{
|
|
||||||
diskInfo: disk.Info{
|
|
||||||
Free: diskMinTotalSpace * 3,
|
|
||||||
FSType: "XFS",
|
|
||||||
Files: 9999,
|
|
||||||
Ffree: 9999,
|
|
||||||
},
|
|
||||||
err: nil,
|
|
||||||
},
|
|
||||||
// Test 3 - when fstype is btrfs and total inodes are empty.
|
|
||||||
{
|
|
||||||
diskInfo: disk.Info{
|
|
||||||
Free: diskMinTotalSpace * 3,
|
|
||||||
FSType: "BTRFS",
|
|
||||||
Files: 0,
|
|
||||||
},
|
|
||||||
err: nil,
|
|
||||||
},
|
|
||||||
// Test 4 - when fstype is xfs and total disk space is really small.
|
|
||||||
{
|
|
||||||
diskInfo: disk.Info{
|
|
||||||
Free: diskMinTotalSpace - diskMinTotalSpace/1024,
|
|
||||||
FSType: "XFS",
|
|
||||||
Files: 9999,
|
|
||||||
},
|
|
||||||
err: errDiskFull,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate all cases.
|
|
||||||
for i, test := range testCases {
|
|
||||||
if err := checkDiskMinFree(test.diskInfo); test.err != err {
|
|
||||||
t.Errorf("Test %d: Expected error %s, got %s", i+1, test.err, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Loading…
Reference in New Issue
Block a user