mirror of
				https://github.com/minio/minio.git
				synced 2025-10-29 15:55:00 -04: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 { | ||||
| 		logger.LogIf(ctx, err) | ||||
| 		return 0, err | ||||
| 	} | ||||
| 
 | ||||
| 	if err := checkDiskFree(pathutil.Dir(filePath), fallocSize); err != nil { | ||||
| 		logger.LogIf(ctx, err) | ||||
| 		switch { | ||||
| 		case os.IsPermission(err): | ||||
| 			return 0, errFileAccessDenied | ||||
| 		case os.IsExist(err): | ||||
| 			return 0, errFileAccessDenied | ||||
| 		case isSysErrIO(err): | ||||
| 			return 0, errFaultyDisk | ||||
| 		case isSysErrInvalidArg(err): | ||||
| 			return 0, errUnsupportedDisk | ||||
| 		case isSysErrNoSpace(err): | ||||
| 			return 0, errDiskFull | ||||
| 		} | ||||
| 		return 0, err | ||||
| 	} | ||||
| 
 | ||||
|  | ||||
| @ -58,7 +58,6 @@ const ( | ||||
| 	globalMinioDefaultOwnerID      = "02d6176db174dc93cb1b899f7c6078f08654445fe8cf1b6ce98d8855f66bdbf4" | ||||
| 	globalMinioDefaultStorageClass = "STANDARD" | ||||
| 	globalWindowsOSName            = "windows" | ||||
| 	globalNetBSDOSName             = "netbsd" | ||||
| 	globalMacOSName                = "darwin" | ||||
| 	globalMinioModeFS              = "mode-server-fs" | ||||
| 	globalMinioModeErasure         = "mode-server-xl" | ||||
|  | ||||
| @ -142,5 +142,14 @@ func osErrToFileErr(err error) error { | ||||
| 	if isSysErrHandleInvalid(err) { | ||||
| 		return errFileNotFound | ||||
| 	} | ||||
| 	if isSysErrIO(err) { | ||||
| 		return errFaultyDisk | ||||
| 	} | ||||
| 	if isSysErrInvalidArg(err) { | ||||
| 		return errUnsupportedDisk | ||||
| 	} | ||||
| 	if isSysErrNoSpace(err) { | ||||
| 		return errDiskFull | ||||
| 	} | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| @ -48,8 +48,7 @@ import ( | ||||
| 
 | ||||
| const ( | ||||
| 	nullVersionID     = "null" | ||||
| 	diskMinFreeSpace  = 900 * humanize.MiByte // Min 900MiB free space. | ||||
| 	diskMinTotalSpace = diskMinFreeSpace      // Min 900MiB total space. | ||||
| 	diskMinTotalSpace = 900 * humanize.MiByte // Min 900MiB total space. | ||||
| 	readBlockSize     = 4 * humanize.MiByte   // Default read block size 4MiB. | ||||
| 
 | ||||
| 	// On regular files bigger than this; | ||||
| @ -175,6 +174,7 @@ func getValidPath(path string, requireDirectIO bool) (string, error) { | ||||
| 	if err != nil { | ||||
| 		return path, err | ||||
| 	} | ||||
| 
 | ||||
| 	if err = checkDiskMinTotal(di); err != nil { | ||||
| 		return path, err | ||||
| 	} | ||||
| @ -276,13 +276,6 @@ func getDiskInfo(diskPath string) (di disk.Info, err error) { | ||||
| 	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. | ||||
| func checkDiskMinTotal(di disk.Info) (err error) { | ||||
| 	// Remove 5% from total space for cumulative disk space | ||||
| @ -294,46 +287,6 @@ func checkDiskMinTotal(di disk.Info) (err error) { | ||||
| 	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. | ||||
| func (s *xlStorage) String() string { | ||||
| 	return s.diskPath | ||||
| @ -1643,14 +1596,6 @@ func (s *xlStorage) CreateFile(volume, path string, fileSize int64, r io.Reader) | ||||
| 		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) | ||||
| 	if err != nil { | ||||
| 		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. | ||||
| 	// with mode 0777 mkdir honors system umask. | ||||
| 	if err = mkdirAll(slashpath.Dir(filePath), 0777); err != nil { | ||||
| 		if errors.Is(err, &os.PathError{}) { | ||||
| 		switch { | ||||
| 		case os.IsPermission(err): | ||||
| 			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 | ||||
| 	} | ||||
| @ -1691,6 +1645,8 @@ func (s *xlStorage) CreateFile(volume, path string, fileSize int64, r io.Reader) | ||||
| 			return errFaultyDisk | ||||
| 		case isSysErrInvalidArg(err): | ||||
| 			return errUnsupportedDisk | ||||
| 		case isSysErrNoSpace(err): | ||||
| 			return errDiskFull | ||||
| 		default: | ||||
| 			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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user