| 
									
										
										
										
											2015-10-17 02:26:01 +08:00
										 |  |  | /* | 
					
						
							| 
									
										
										
										
											2019-05-15 04:49:10 +08:00
										 |  |  |  * MinIO Cloud Storage, (C) 2016-2019 MinIO, Inc. | 
					
						
							| 
									
										
										
										
											2015-10-17 02:26:01 +08:00
										 |  |  |  * | 
					
						
							|  |  |  |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
					
						
							|  |  |  |  * you may not use this file except in compliance with the License. | 
					
						
							|  |  |  |  * You may obtain a copy of the License at | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *     http://www.apache.org/licenses/LICENSE-2.0
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Unless required by applicable law or agreed to in writing, software | 
					
						
							|  |  |  |  * distributed under the License is distributed on an "AS IS" BASIS, | 
					
						
							|  |  |  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
					
						
							|  |  |  |  * See the License for the specific language governing permissions and | 
					
						
							|  |  |  |  * limitations under the License. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-19 07:23:42 +08:00
										 |  |  | package cmd | 
					
						
							| 
									
										
										
										
											2015-10-17 02:26:01 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2017-05-17 05:21:52 +08:00
										 |  |  | 	"encoding/hex" | 
					
						
							| 
									
										
										
										
											2016-04-09 01:37:38 +08:00
										 |  |  | 	"io" | 
					
						
							| 
									
										
										
										
											2016-06-26 05:51:06 +08:00
										 |  |  | 	"io/ioutil" | 
					
						
							| 
									
										
										
										
											2016-04-09 08:13:16 +08:00
										 |  |  | 	"os" | 
					
						
							| 
									
										
										
										
											2016-05-09 15:46:54 +08:00
										 |  |  | 	slashpath "path" | 
					
						
							| 
									
										
										
										
											2016-05-29 06:13:15 +08:00
										 |  |  | 	"path/filepath" | 
					
						
							| 
									
										
										
										
											2016-06-20 21:18:47 +08:00
										 |  |  | 	"runtime" | 
					
						
							| 
									
										
										
										
											2019-05-15 04:49:10 +08:00
										 |  |  | 	"sort" | 
					
						
							| 
									
										
										
										
											2018-02-21 07:33:26 +08:00
										 |  |  | 	"strings" | 
					
						
							| 
									
										
										
										
											2016-10-27 08:14:05 +08:00
										 |  |  | 	"sync" | 
					
						
							| 
									
										
										
										
											2016-06-21 07:57:14 +08:00
										 |  |  | 	"sync/atomic" | 
					
						
							| 
									
										
										
										
											2016-04-09 01:37:38 +08:00
										 |  |  | 	"syscall" | 
					
						
							| 
									
										
										
										
											2018-05-23 18:11:29 +08:00
										 |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2015-10-17 02:26:01 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-07 06:14:08 +08:00
										 |  |  | 	"bytes" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-23 10:18:22 +08:00
										 |  |  | 	humanize "github.com/dustin/go-humanize" | 
					
						
							| 
									
										
										
										
											2019-05-23 04:47:15 +08:00
										 |  |  | 	"github.com/klauspost/readahead" | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 	"github.com/minio/minio/cmd/logger" | 
					
						
							| 
									
										
										
										
											2016-04-09 08:13:16 +08:00
										 |  |  | 	"github.com/minio/minio/pkg/disk" | 
					
						
							| 
									
										
										
										
											2019-05-23 04:47:15 +08:00
										 |  |  | 	xioutil "github.com/minio/minio/pkg/ioutil" | 
					
						
							| 
									
										
										
										
											2018-06-28 09:59:38 +08:00
										 |  |  | 	"github.com/minio/minio/pkg/mountinfo" | 
					
						
							| 
									
										
										
										
											2019-04-24 12:25:06 +08:00
										 |  |  | 	"github.com/ncw/directio" | 
					
						
							| 
									
										
										
										
											2015-10-17 02:26:01 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-09 01:37:38 +08:00
										 |  |  | const ( | 
					
						
							| 
									
										
										
										
											2019-05-23 04:47:15 +08:00
										 |  |  | 	diskMinFreeSpace  = 900 * humanize.MiByte // Min 900MiB free space.
 | 
					
						
							|  |  |  | 	diskMinTotalSpace = diskMinFreeSpace      // Min 900MiB total space.
 | 
					
						
							|  |  |  | 	maxAllowedIOError = 5 | 
					
						
							|  |  |  | 	readBlockSize     = humanize.KiByte * 32 // Default read block size 32KiB.
 | 
					
						
							| 
									
										
										
										
											2016-04-09 01:37:38 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-07 15:01:40 +08:00
										 |  |  | // isValidVolname verifies a volname name in accordance with object
 | 
					
						
							|  |  |  | // layer requirements.
 | 
					
						
							|  |  |  | func isValidVolname(volname string) bool { | 
					
						
							|  |  |  | 	if len(volname) < 3 { | 
					
						
							|  |  |  | 		return false | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if runtime.GOOS == "windows" { | 
					
						
							|  |  |  | 		// Volname shouldn't have reserved characters in Windows.
 | 
					
						
							|  |  |  | 		return !strings.ContainsAny(volname, `\:*?\"<>|`) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return true | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-29 06:13:15 +08:00
										 |  |  | // posix - implements StorageAPI interface.
 | 
					
						
							|  |  |  | type posix struct { | 
					
						
							| 
									
										
										
										
											2018-06-05 09:35:41 +08:00
										 |  |  | 	// Disk usage metrics
 | 
					
						
							| 
									
										
										
										
											2018-06-29 06:05:45 +08:00
										 |  |  | 	totalUsed  uint64 // ref: https://golang.org/pkg/sync/atomic/#pkg-note-BUG
 | 
					
						
							|  |  |  | 	ioErrCount int32  // ref: https://golang.org/pkg/sync/atomic/#pkg-note-BUG
 | 
					
						
							| 
									
										
										
										
											2018-06-05 09:35:41 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	diskPath  string | 
					
						
							|  |  |  | 	pool      sync.Pool | 
					
						
							|  |  |  | 	connected bool | 
					
						
							| 
									
										
										
										
											2018-05-23 18:11:29 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-28 09:59:38 +08:00
										 |  |  | 	diskMount bool // indicates if the path is an actual mount.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-21 05:32:29 +08:00
										 |  |  | 	diskFileInfo os.FileInfo | 
					
						
							| 
									
										
										
										
											2018-05-23 18:11:29 +08:00
										 |  |  | 	// Disk usage metrics
 | 
					
						
							| 
									
										
										
										
											2018-06-05 09:35:41 +08:00
										 |  |  | 	stopUsageCh chan struct{} | 
					
						
							| 
									
										
										
										
											2016-03-29 00:52:09 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-12 03:55:02 +08:00
										 |  |  | // checkPathLength - returns error if given path name length more than 255
 | 
					
						
							|  |  |  | func checkPathLength(pathName string) error { | 
					
						
							| 
									
										
										
										
											2016-07-04 02:17:08 +08:00
										 |  |  | 	// Apple OS X path length is limited to 1016
 | 
					
						
							|  |  |  | 	if runtime.GOOS == "darwin" && len(pathName) > 1016 { | 
					
						
							|  |  |  | 		return errFileNameTooLong | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-26 13:05:14 +08:00
										 |  |  | 	if runtime.GOOS == "windows" { | 
					
						
							|  |  |  | 		// Convert any '\' to '/'.
 | 
					
						
							|  |  |  | 		pathName = filepath.ToSlash(pathName) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-11-01 00:34:44 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-13 17:53:09 +08:00
										 |  |  | 	// Check each path segment length is > 255
 | 
					
						
							| 
									
										
										
										
											2016-05-12 03:55:02 +08:00
										 |  |  | 	for len(pathName) > 0 && pathName != "." && pathName != "/" { | 
					
						
							|  |  |  | 		dir, file := slashpath.Dir(pathName), slashpath.Base(pathName) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if len(file) > 255 { | 
					
						
							|  |  |  | 			return errFileNameTooLong | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		pathName = dir | 
					
						
							| 
									
										
										
										
											2016-06-13 17:53:09 +08:00
										 |  |  | 	} // Success.
 | 
					
						
							| 
									
										
										
										
											2016-05-12 03:55:02 +08:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-17 22:27:07 +08:00
										 |  |  | func getValidPath(path string) (string, error) { | 
					
						
							| 
									
										
										
										
											2018-04-10 11:56:09 +08:00
										 |  |  | 	if path == "" { | 
					
						
							|  |  |  | 		return path, errInvalidArgument | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var err error | 
					
						
							|  |  |  | 	// Disallow relative paths, figure out absolute paths.
 | 
					
						
							|  |  |  | 	path, err = filepath.Abs(path) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return path, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	fi, err := os.Stat(path) | 
					
						
							|  |  |  | 	if err != nil && !os.IsNotExist(err) { | 
					
						
							|  |  |  | 		return path, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if os.IsNotExist(err) { | 
					
						
							|  |  |  | 		// Disk not found create it.
 | 
					
						
							|  |  |  | 		if err = os.MkdirAll(path, 0777); err != nil { | 
					
						
							|  |  |  | 			return path, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if fi != nil && !fi.IsDir() { | 
					
						
							|  |  |  | 		return path, syscall.ENOTDIR | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	di, err := getDiskInfo(path) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return path, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if err = checkDiskMinTotal(di); err != nil { | 
					
						
							|  |  |  | 		return path, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// check if backend is writable.
 | 
					
						
							|  |  |  | 	file, err := os.Create(pathJoin(path, ".writable-check.tmp")) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return path, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-06-13 07:36:31 +08:00
										 |  |  | 	defer os.Remove(pathJoin(path, ".writable-check.tmp")) | 
					
						
							| 
									
										
										
										
											2018-04-10 11:56:09 +08:00
										 |  |  | 	file.Close() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-13 07:36:31 +08:00
										 |  |  | 	return path, nil | 
					
						
							| 
									
										
										
										
											2018-04-10 11:56:09 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-09 01:37:38 +08:00
										 |  |  | // isDirEmpty - returns whether given directory is empty or not.
 | 
					
						
							| 
									
										
										
										
											2016-05-08 16:58:05 +08:00
										 |  |  | func isDirEmpty(dirname string) bool { | 
					
						
							| 
									
										
										
										
											2017-08-13 10:25:43 +08:00
										 |  |  | 	f, err := os.Open((dirname)) | 
					
						
							| 
									
										
										
										
											2016-05-08 16:58:05 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2017-08-05 01:43:51 +08:00
										 |  |  | 		if !os.IsNotExist(err) { | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 			logger.LogIf(context.Background(), err) | 
					
						
							| 
									
										
										
										
											2017-08-05 01:43:51 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-08 16:58:05 +08:00
										 |  |  | 		return false | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	defer f.Close() | 
					
						
							|  |  |  | 	// List one entry.
 | 
					
						
							|  |  |  | 	_, err = f.Readdirnames(1) | 
					
						
							| 
									
										
										
										
											2016-10-21 00:26:18 +08:00
										 |  |  | 	if err != io.EOF { | 
					
						
							| 
									
										
										
										
											2017-08-05 01:43:51 +08:00
										 |  |  | 		if !os.IsNotExist(err) { | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 			logger.LogIf(context.Background(), err) | 
					
						
							| 
									
										
										
										
											2017-08-05 01:43:51 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-08 16:58:05 +08:00
										 |  |  | 		return false | 
					
						
							| 
									
										
										
										
											2016-04-09 01:37:38 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-10-21 00:26:18 +08:00
										 |  |  | 	// Returns true if we have reached EOF, directory is indeed empty.
 | 
					
						
							|  |  |  | 	return true | 
					
						
							| 
									
										
										
										
											2016-02-18 16:38:58 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-09 01:37:38 +08:00
										 |  |  | // Initialize a new storage disk.
 | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | func newPosix(path string) (*posix, error) { | 
					
						
							| 
									
										
										
										
											2018-04-10 11:56:09 +08:00
										 |  |  | 	var err error | 
					
						
							| 
									
										
										
										
											2018-05-17 22:27:07 +08:00
										 |  |  | 	if path, err = getValidPath(path); err != nil { | 
					
						
							| 
									
										
										
										
											2016-06-13 17:53:09 +08:00
										 |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-02-21 05:32:29 +08:00
										 |  |  | 	fi, err := os.Stat(path) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | 	p := &posix{ | 
					
						
							|  |  |  | 		connected: true, | 
					
						
							|  |  |  | 		diskPath:  path, | 
					
						
							| 
									
										
										
										
											2016-10-27 08:14:05 +08:00
										 |  |  | 		pool: sync.Pool{ | 
					
						
							|  |  |  | 			New: func() interface{} { | 
					
						
							| 
									
										
										
										
											2019-05-23 04:47:15 +08:00
										 |  |  | 				b := directio.AlignedBlock(readBlockSize) | 
					
						
							| 
									
										
										
										
											2016-10-27 08:14:05 +08:00
										 |  |  | 				return &b | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2019-02-21 05:32:29 +08:00
										 |  |  | 		stopUsageCh:  make(chan struct{}), | 
					
						
							|  |  |  | 		diskFileInfo: fi, | 
					
						
							|  |  |  | 		diskMount:    mountinfo.IsLikelyMountPoint(path), | 
					
						
							| 
									
										
										
										
											2016-05-12 03:55:02 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-05-23 18:11:29 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-28 09:59:38 +08:00
										 |  |  | 	if !p.diskMount { | 
					
						
							| 
									
										
										
										
											2019-01-06 06:16:43 +08:00
										 |  |  | 		go p.diskUsage(GlobalServiceDoneCh) | 
					
						
							| 
									
										
										
										
											2018-06-28 09:59:38 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-05-23 18:11:29 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-11 09:14:48 +08:00
										 |  |  | 	// Success.
 | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | 	return p, nil | 
					
						
							| 
									
										
										
										
											2016-04-09 01:37:38 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2016-01-25 15:03:38 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-30 02:25:35 +08:00
										 |  |  | // getDiskInfo returns given disk information.
 | 
					
						
							|  |  |  | func getDiskInfo(diskPath string) (di disk.Info, err error) { | 
					
						
							|  |  |  | 	if err = checkPathLength(diskPath); err == nil { | 
					
						
							|  |  |  | 		di, err = disk.GetInfo(diskPath) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if os.IsNotExist(err) { | 
					
						
							|  |  |  | 		err = errDiskNotFound | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return di, err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-09 14:27:35 +08:00
										 |  |  | // List of operating systems where we ignore disk space
 | 
					
						
							|  |  |  | // verification.
 | 
					
						
							|  |  |  | var ignoreDiskFreeOS = []string{ | 
					
						
							|  |  |  | 	globalWindowsOSName, | 
					
						
							|  |  |  | 	globalNetBSDOSName, | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-11 09:14:48 +08:00
										 |  |  | // check if disk total has minimum required size.
 | 
					
						
							|  |  |  | func checkDiskMinTotal(di disk.Info) (err error) { | 
					
						
							|  |  |  | 	// Remove 5% from total space for cumulative disk space
 | 
					
						
							|  |  |  | 	// used for journalling, inodes etc.
 | 
					
						
							|  |  |  | 	totalDiskSpace := float64(di.Total) * 0.95 | 
					
						
							|  |  |  | 	if int64(totalDiskSpace) <= diskMinTotalSpace { | 
					
						
							| 
									
										
										
										
											2018-06-29 03:01:05 +08:00
										 |  |  | 		return errMinDiskSize | 
					
						
							| 
									
										
										
										
											2016-11-01 00:34:44 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-07-11 09:14:48 +08:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // check if disk free has minimum required size.
 | 
					
						
							|  |  |  | func checkDiskMinFree(di disk.Info) error { | 
					
						
							| 
									
										
										
										
											2016-09-28 03:46:38 +08:00
										 |  |  | 	// Remove 5% from free space for cumulative disk space used for journalling, inodes etc.
 | 
					
						
							|  |  |  | 	availableDiskSpace := float64(di.Free) * 0.95 | 
					
						
							| 
									
										
										
										
											2017-07-11 09:14:48 +08:00
										 |  |  | 	if int64(availableDiskSpace) <= diskMinFreeSpace { | 
					
						
							| 
									
										
										
										
											2016-04-19 17:42:10 +08:00
										 |  |  | 		return errDiskFull | 
					
						
							| 
									
										
										
										
											2016-04-09 01:37:38 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-03-29 00:52:09 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-11 09:14:48 +08:00
										 |  |  | 	// 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 | 
					
						
							| 
									
										
										
										
											2017-08-13 10:25:43 +08:00
										 |  |  | 	di, err = getDiskInfo((diskPath)) | 
					
						
							| 
									
										
										
										
											2017-07-11 09:14:48 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if err = checkDiskMinFree(di); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-08 04:25:40 +08:00
										 |  |  | 	// Check if we have enough space to store data
 | 
					
						
							| 
									
										
										
										
											2017-07-11 09:14:48 +08:00
										 |  |  | 	if neededSpace > int64(float64(di.Free)*0.95) { | 
					
						
							| 
									
										
										
										
											2017-03-08 04:25:40 +08:00
										 |  |  | 		return errDiskFull | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-09 01:37:38 +08:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-06 03:48:07 +08:00
										 |  |  | // Implements stringer compatible interface.
 | 
					
						
							|  |  |  | func (s *posix) String() string { | 
					
						
							| 
									
										
										
										
											2016-10-27 18:30:52 +08:00
										 |  |  | 	return s.diskPath | 
					
						
							| 
									
										
										
										
											2016-10-06 03:48:07 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-11 07:21:59 +08:00
										 |  |  | func (s *posix) LastError() error { | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-16 09:45:57 +08:00
										 |  |  | func (s *posix) Close() error { | 
					
						
							| 
									
										
										
										
											2018-05-23 18:11:29 +08:00
										 |  |  | 	close(s.stopUsageCh) | 
					
						
							| 
									
										
										
										
											2018-02-16 09:45:57 +08:00
										 |  |  | 	s.connected = false | 
					
						
							| 
									
										
										
										
											2016-11-24 07:48:10 +08:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-16 09:45:57 +08:00
										 |  |  | func (s *posix) IsOnline() bool { | 
					
						
							|  |  |  | 	return s.connected | 
					
						
							| 
									
										
										
										
											2016-11-24 07:48:10 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-23 18:11:29 +08:00
										 |  |  | // DiskInfo is an extended type which returns current
 | 
					
						
							|  |  |  | // disk usage per path.
 | 
					
						
							|  |  |  | type DiskInfo struct { | 
					
						
							| 
									
										
										
										
											2019-01-24 07:29:29 +08:00
										 |  |  | 	Total    uint64 | 
					
						
							|  |  |  | 	Free     uint64 | 
					
						
							|  |  |  | 	Used     uint64 | 
					
						
							|  |  |  | 	RootDisk bool | 
					
						
							| 
									
										
										
										
											2018-05-23 18:11:29 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-26 08:16:34 +08:00
										 |  |  | // DiskInfo provides current information about disk space usage,
 | 
					
						
							|  |  |  | // total free inodes and underlying filesystem.
 | 
					
						
							| 
									
										
										
										
											2018-05-23 18:11:29 +08:00
										 |  |  | func (s *posix) DiskInfo() (info DiskInfo, err error) { | 
					
						
							|  |  |  | 	di, err := getDiskInfo(s.diskPath) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return info, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-06-28 09:59:38 +08:00
										 |  |  | 	used := di.Total - di.Free | 
					
						
							|  |  |  | 	if !s.diskMount { | 
					
						
							|  |  |  | 		used = atomic.LoadUint64(&s.totalUsed) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-01-24 07:29:29 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	rootDisk, err := disk.IsRootDisk(s.diskPath) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return info, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-05-23 18:11:29 +08:00
										 |  |  | 	return DiskInfo{ | 
					
						
							| 
									
										
										
										
											2019-01-24 07:29:29 +08:00
										 |  |  | 		Total:    di.Total, | 
					
						
							|  |  |  | 		Free:     di.Free, | 
					
						
							|  |  |  | 		Used:     used, | 
					
						
							|  |  |  | 		RootDisk: rootDisk, | 
					
						
							| 
									
										
										
										
											2018-05-23 18:11:29 +08:00
										 |  |  | 	}, nil | 
					
						
							| 
									
										
										
										
											2016-08-26 08:16:34 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-18 12:22:27 +08:00
										 |  |  | // getVolDir - will convert incoming volume names to
 | 
					
						
							| 
									
										
										
										
											2016-04-14 02:32:47 +08:00
										 |  |  | // corresponding valid volume names on the backend in a platform
 | 
					
						
							|  |  |  | // compatible way for all operating systems. If volume is not found
 | 
					
						
							|  |  |  | // an error is generated.
 | 
					
						
							| 
									
										
										
										
											2016-06-21 07:57:14 +08:00
										 |  |  | func (s *posix) getVolDir(volume string) (string, error) { | 
					
						
							| 
									
										
										
										
											2018-04-24 11:27:33 +08:00
										 |  |  | 	if volume == "" || volume == "." || volume == ".." { | 
					
						
							|  |  |  | 		return "", errVolumeNotFound | 
					
						
							| 
									
										
										
										
											2016-04-14 02:32:47 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-05-05 16:39:26 +08:00
										 |  |  | 	volumeDir := pathJoin(s.diskPath, volume) | 
					
						
							| 
									
										
										
										
											2016-05-18 12:22:27 +08:00
										 |  |  | 	return volumeDir, nil | 
					
						
							| 
									
										
										
										
											2016-04-14 02:32:47 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2016-04-09 01:37:38 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-01 00:34:44 +08:00
										 |  |  | // checkDiskFound - validates if disk is available,
 | 
					
						
							|  |  |  | // returns errDiskNotFound if not found.
 | 
					
						
							|  |  |  | func (s *posix) checkDiskFound() (err error) { | 
					
						
							| 
									
										
										
										
											2018-02-16 09:45:57 +08:00
										 |  |  | 	if !s.IsOnline() { | 
					
						
							|  |  |  | 		return errDiskNotFound | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-02-21 05:32:29 +08:00
										 |  |  | 	fi, err := os.Stat(s.diskPath) | 
					
						
							| 
									
										
										
										
											2016-11-01 00:34:44 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2018-08-07 01:26:40 +08:00
										 |  |  | 		switch { | 
					
						
							|  |  |  | 		case os.IsNotExist(err): | 
					
						
							| 
									
										
										
										
											2016-11-01 00:34:44 +08:00
										 |  |  | 			return errDiskNotFound | 
					
						
							| 
									
										
										
										
											2018-08-07 01:26:40 +08:00
										 |  |  | 		case isSysErrTooLong(err): | 
					
						
							| 
									
										
										
										
											2016-11-01 00:34:44 +08:00
										 |  |  | 			return errFileNameTooLong | 
					
						
							| 
									
										
										
										
											2018-08-07 01:26:40 +08:00
										 |  |  | 		case isSysErrIO(err): | 
					
						
							| 
									
										
										
										
											2018-07-28 06:32:19 +08:00
										 |  |  | 			return errFaultyDisk | 
					
						
							| 
									
										
										
										
											2018-08-07 01:26:40 +08:00
										 |  |  | 		default: | 
					
						
							|  |  |  | 			return err | 
					
						
							| 
									
										
										
										
											2016-11-01 00:34:44 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-02-21 05:32:29 +08:00
										 |  |  | 	if !os.SameFile(s.diskFileInfo, fi) { | 
					
						
							|  |  |  | 		s.connected = false | 
					
						
							|  |  |  | 		return errDiskNotFound | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-07-28 06:32:19 +08:00
										 |  |  | 	return nil | 
					
						
							| 
									
										
										
										
											2016-11-01 00:34:44 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-23 18:11:29 +08:00
										 |  |  | // diskUsage returns du information for the posix path, in a continuous routine.
 | 
					
						
							| 
									
										
										
										
											2018-06-05 09:35:41 +08:00
										 |  |  | func (s *posix) diskUsage(doneCh chan struct{}) { | 
					
						
							|  |  |  | 	ticker := time.NewTicker(globalUsageCheckInterval) | 
					
						
							| 
									
										
										
										
											2018-05-23 18:11:29 +08:00
										 |  |  | 	defer ticker.Stop() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	usageFn := func(ctx context.Context, entry string) error { | 
					
						
							| 
									
										
										
										
											2018-06-28 09:59:38 +08:00
										 |  |  | 		if globalHTTPServer != nil { | 
					
						
							| 
									
										
										
										
											2018-06-29 06:05:45 +08:00
										 |  |  | 			// Wait at max 1 minute for an inprogress request
 | 
					
						
							|  |  |  | 			// before proceeding to count the usage.
 | 
					
						
							|  |  |  | 			waitCount := 60 | 
					
						
							| 
									
										
										
										
											2018-06-28 09:59:38 +08:00
										 |  |  | 			// Any requests in progress, delay the usage.
 | 
					
						
							| 
									
										
										
										
											2018-06-29 06:05:45 +08:00
										 |  |  | 			for globalHTTPServer.GetRequestCount() > 0 && waitCount > 0 { | 
					
						
							|  |  |  | 				waitCount-- | 
					
						
							| 
									
										
										
										
											2018-06-28 09:59:38 +08:00
										 |  |  | 				time.Sleep(1 * time.Second) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-23 18:11:29 +08:00
										 |  |  | 		select { | 
					
						
							| 
									
										
										
										
											2018-06-28 09:59:38 +08:00
										 |  |  | 		case <-doneCh: | 
					
						
							|  |  |  | 			return errWalkAbort | 
					
						
							| 
									
										
										
										
											2018-05-23 18:11:29 +08:00
										 |  |  | 		case <-s.stopUsageCh: | 
					
						
							|  |  |  | 			return errWalkAbort | 
					
						
							|  |  |  | 		default: | 
					
						
							|  |  |  | 			fi, err := os.Stat(entry) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2018-11-26 13:05:14 +08:00
										 |  |  | 				err = osErrToFSFileErr(err) | 
					
						
							| 
									
										
										
										
											2018-05-23 18:11:29 +08:00
										 |  |  | 				return err | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2018-06-05 09:35:41 +08:00
										 |  |  | 			atomic.AddUint64(&s.totalUsed, uint64(fi.Size())) | 
					
						
							| 
									
										
										
										
											2018-05-23 18:11:29 +08:00
										 |  |  | 			return nil | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-29 06:05:45 +08:00
										 |  |  | 	// Return this routine upon errWalkAbort, continue for any other error on purpose
 | 
					
						
							|  |  |  | 	// so that we can start the routine freshly in another 12 hours.
 | 
					
						
							|  |  |  | 	if err := getDiskUsage(context.Background(), s.diskPath, usageFn); err == errWalkAbort { | 
					
						
							| 
									
										
										
										
											2018-06-05 09:35:41 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-05-23 18:11:29 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	for { | 
					
						
							|  |  |  | 		select { | 
					
						
							|  |  |  | 		case <-s.stopUsageCh: | 
					
						
							|  |  |  | 			return | 
					
						
							| 
									
										
										
										
											2018-06-05 09:35:41 +08:00
										 |  |  | 		case <-doneCh: | 
					
						
							| 
									
										
										
										
											2018-05-23 18:11:29 +08:00
										 |  |  | 			return | 
					
						
							| 
									
										
										
										
											2018-06-29 06:05:45 +08:00
										 |  |  | 		case <-time.After(globalUsageCheckInterval): | 
					
						
							| 
									
										
										
										
											2018-05-24 08:30:25 +08:00
										 |  |  | 			var usage uint64 | 
					
						
							| 
									
										
										
										
											2018-05-23 18:11:29 +08:00
										 |  |  | 			usageFn = func(ctx context.Context, entry string) error { | 
					
						
							| 
									
										
										
										
											2018-06-28 09:59:38 +08:00
										 |  |  | 				if globalHTTPServer != nil { | 
					
						
							| 
									
										
										
										
											2018-06-29 06:05:45 +08:00
										 |  |  | 					// Wait at max 1 minute for an inprogress request
 | 
					
						
							|  |  |  | 					// before proceeding to count the usage.
 | 
					
						
							|  |  |  | 					waitCount := 60 | 
					
						
							| 
									
										
										
										
											2018-06-28 09:59:38 +08:00
										 |  |  | 					// Any requests in progress, delay the usage.
 | 
					
						
							| 
									
										
										
										
											2018-06-29 06:05:45 +08:00
										 |  |  | 					for globalHTTPServer.GetRequestCount() > 0 && waitCount > 0 { | 
					
						
							|  |  |  | 						waitCount-- | 
					
						
							| 
									
										
										
										
											2018-06-28 09:59:38 +08:00
										 |  |  | 						time.Sleep(1 * time.Second) | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-23 18:11:29 +08:00
										 |  |  | 				select { | 
					
						
							|  |  |  | 				case <-s.stopUsageCh: | 
					
						
							|  |  |  | 					return errWalkAbort | 
					
						
							|  |  |  | 				default: | 
					
						
							|  |  |  | 					fi, err := os.Stat(entry) | 
					
						
							|  |  |  | 					if err != nil { | 
					
						
							| 
									
										
										
										
											2018-11-26 13:05:14 +08:00
										 |  |  | 						err = osErrToFSFileErr(err) | 
					
						
							| 
									
										
										
										
											2018-05-23 18:11:29 +08:00
										 |  |  | 						return err | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					usage = usage + uint64(fi.Size()) | 
					
						
							|  |  |  | 					return nil | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2018-06-05 09:35:41 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-23 18:11:29 +08:00
										 |  |  | 			if err := getDiskUsage(context.Background(), s.diskPath, usageFn); err != nil { | 
					
						
							|  |  |  | 				continue | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2018-06-05 09:35:41 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			atomic.StoreUint64(&s.totalUsed, usage) | 
					
						
							| 
									
										
										
										
											2018-05-23 18:11:29 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-14 02:32:47 +08:00
										 |  |  | // Make a volume entry.
 | 
					
						
							| 
									
										
										
										
											2016-06-21 07:57:14 +08:00
										 |  |  | func (s *posix) MakeVol(volume string) (err error) { | 
					
						
							| 
									
										
										
										
											2016-06-09 13:02:10 +08:00
										 |  |  | 	defer func() { | 
					
						
							| 
									
										
										
										
											2018-07-28 06:32:19 +08:00
										 |  |  | 		if err == errFaultyDisk { | 
					
						
							| 
									
										
										
										
											2016-06-21 07:57:14 +08:00
										 |  |  | 			atomic.AddInt32(&s.ioErrCount, 1) | 
					
						
							| 
									
										
										
										
											2016-06-09 13:02:10 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-01 00:22:53 +08:00
										 |  |  | 	if atomic.LoadInt32(&s.ioErrCount) > maxAllowedIOError { | 
					
						
							| 
									
										
										
										
											2016-06-09 13:02:10 +08:00
										 |  |  | 		return errFaultyDisk | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-01 00:34:44 +08:00
										 |  |  | 	if err = s.checkDiskFound(); err != nil { | 
					
						
							| 
									
										
										
										
											2016-04-24 15:36:00 +08:00
										 |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-24 11:27:33 +08:00
										 |  |  | 	if !isValidVolname(volume) { | 
					
						
							|  |  |  | 		return errInvalidArgument | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-18 12:22:27 +08:00
										 |  |  | 	volumeDir, err := s.getVolDir(volume) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							| 
									
										
										
										
											2016-04-09 01:37:38 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-01-30 10:43:13 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if _, err := os.Stat(volumeDir); err != nil { | 
					
						
							|  |  |  | 		// Volume does not exist we proceed to create.
 | 
					
						
							|  |  |  | 		if os.IsNotExist(err) { | 
					
						
							|  |  |  | 			// Make a volume entry, with mode 0777 mkdir honors system umask.
 | 
					
						
							|  |  |  | 			err = os.MkdirAll(volumeDir, 0777) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if os.IsPermission(err) { | 
					
						
							| 
									
										
										
										
											2016-07-02 16:59:28 +08:00
										 |  |  | 			return errDiskAccessDenied | 
					
						
							| 
									
										
										
										
											2018-07-28 06:32:19 +08:00
										 |  |  | 		} else if isSysErrIO(err) { | 
					
						
							|  |  |  | 			return errFaultyDisk | 
					
						
							| 
									
										
										
										
											2016-07-02 16:59:28 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		return err | 
					
						
							| 
									
										
										
										
											2016-04-14 02:32:47 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-01-30 10:43:13 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Stat succeeds we return errVolumeExists.
 | 
					
						
							|  |  |  | 	return errVolumeExists | 
					
						
							| 
									
										
										
										
											2016-04-09 01:37:38 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ListVols - list volumes.
 | 
					
						
							| 
									
										
										
										
											2016-06-21 07:57:14 +08:00
										 |  |  | func (s *posix) ListVols() (volsInfo []VolInfo, err error) { | 
					
						
							| 
									
										
										
										
											2016-06-09 13:02:10 +08:00
										 |  |  | 	defer func() { | 
					
						
							| 
									
										
										
										
											2018-07-28 06:32:19 +08:00
										 |  |  | 		if err == errFaultyDisk { | 
					
						
							| 
									
										
										
										
											2016-06-21 07:57:14 +08:00
										 |  |  | 			atomic.AddInt32(&s.ioErrCount, 1) | 
					
						
							| 
									
										
										
										
											2016-06-09 13:02:10 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-01 00:22:53 +08:00
										 |  |  | 	if atomic.LoadInt32(&s.ioErrCount) > maxAllowedIOError { | 
					
						
							| 
									
										
										
										
											2016-06-09 13:02:10 +08:00
										 |  |  | 		return nil, errFaultyDisk | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-01 00:34:44 +08:00
										 |  |  | 	if err = s.checkDiskFound(); err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-28 06:32:19 +08:00
										 |  |  | 	volsInfo, err = listVols(s.diskPath) | 
					
						
							| 
									
										
										
										
											2016-04-09 01:37:38 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2018-07-28 06:32:19 +08:00
										 |  |  | 		if isSysErrIO(err) { | 
					
						
							|  |  |  | 			return nil, errFaultyDisk | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-04-09 01:37:38 +08:00
										 |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-04-18 03:00:23 +08:00
										 |  |  | 	for i, vol := range volsInfo { | 
					
						
							| 
									
										
										
										
											2016-04-09 01:37:38 +08:00
										 |  |  | 		volInfo := VolInfo{ | 
					
						
							| 
									
										
										
										
											2016-05-20 09:52:55 +08:00
										 |  |  | 			Name:    vol.Name, | 
					
						
							| 
									
										
										
										
											2016-04-17 03:48:41 +08:00
										 |  |  | 			Created: vol.Created, | 
					
						
							| 
									
										
										
										
											2016-04-09 01:37:38 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-04-18 03:00:23 +08:00
										 |  |  | 		volsInfo[i] = volInfo | 
					
						
							| 
									
										
										
										
											2016-04-09 01:37:38 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return volsInfo, nil | 
					
						
							| 
									
										
										
										
											2015-10-17 02:26:01 +08:00
										 |  |  | } | 
					
						
							| 
									
										
											  
											
												config/main: Re-write config files - add to new config v3
- New config format.
```
{
	"version": "3",
	"address": ":9000",
    "backend": {
          "type": "fs",
          "disk": "/path"
    },
	"credential": {
		"accessKey": "WLGDGYAQYIGI833EV05A",
		"secretKey": "BYvgJM101sHngl2uzjXS/OBF/aMxAN06JrJ3qJlF"
	},
	"region": "us-east-1",
	"logger": {
		"file": {
			"enable": false,
			"fileName": "",
			"level": "error"
		},
		"syslog": {
			"enable": false,
			"address": "",
			"level": "debug"
		},
		"console": {
			"enable": true,
			"level": "fatal"
		}
	}
}
```
New command lines in lieu of supporting XL.
Minio initialize filesystem backend.
~~~
$ minio init fs <path>
~~~
Minio initialize XL backend.
~~~
$ minio init xl <url1>...<url16>
~~~
For 'fs' backend it starts the server.
~~~
$ minio server
~~~
For 'xl' backend it waits for servers to join.
~~~
$ minio server
... [PROGRESS BAR] of servers connecting
~~~
Now on other servers execute 'join' and they connect.
~~~
....
minio join <url1> -- from <url2> && minio server
minio join <url1> -- from <url3> && minio server
...
...
minio join <url1> -- from <url16> && minio server
~~~
											
										 
											2016-02-13 07:27:10 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 10:57:14 +08:00
										 |  |  | // List all the volumes from diskPath.
 | 
					
						
							|  |  |  | func listVols(dirPath string) ([]VolInfo, error) { | 
					
						
							|  |  |  | 	if err := checkPathLength(dirPath); err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	entries, err := readDir(dirPath) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, errDiskNotFound | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	var volsInfo []VolInfo | 
					
						
							|  |  |  | 	for _, entry := range entries { | 
					
						
							| 
									
										
										
										
											2017-02-17 06:52:14 +08:00
										 |  |  | 		if !hasSuffix(entry, slashSeparator) || !isValidVolname(slashpath.Clean(entry)) { | 
					
						
							| 
									
										
										
										
											2016-08-12 10:57:14 +08:00
										 |  |  | 			// Skip if entry is neither a directory not a valid volume name.
 | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		var fi os.FileInfo | 
					
						
							| 
									
										
										
										
											2018-07-28 06:32:19 +08:00
										 |  |  | 		fi, err = os.Stat(pathJoin(dirPath, entry)) | 
					
						
							| 
									
										
										
										
											2016-08-12 10:57:14 +08:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			// If the file does not exist, skip the entry.
 | 
					
						
							|  |  |  | 			if os.IsNotExist(err) { | 
					
						
							|  |  |  | 				continue | 
					
						
							| 
									
										
										
										
											2018-07-28 06:32:19 +08:00
										 |  |  | 			} else if isSysErrIO(err) { | 
					
						
							|  |  |  | 				return nil, errFaultyDisk | 
					
						
							| 
									
										
										
										
											2016-08-12 10:57:14 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		volsInfo = append(volsInfo, VolInfo{ | 
					
						
							|  |  |  | 			Name: fi.Name(), | 
					
						
							| 
									
										
										
										
											2017-10-13 18:01:15 +08:00
										 |  |  | 			// As os.Stat() doesn't carry other than ModTime(), use
 | 
					
						
							| 
									
										
										
										
											2016-08-12 10:57:14 +08:00
										 |  |  | 			// ModTime() as CreatedTime.
 | 
					
						
							|  |  |  | 			Created: fi.ModTime(), | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return volsInfo, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-09 01:37:38 +08:00
										 |  |  | // StatVol - get volume info.
 | 
					
						
							| 
									
										
										
										
											2016-06-21 07:57:14 +08:00
										 |  |  | func (s *posix) StatVol(volume string) (volInfo VolInfo, err error) { | 
					
						
							| 
									
										
										
										
											2016-06-09 13:02:10 +08:00
										 |  |  | 	defer func() { | 
					
						
							| 
									
										
										
										
											2018-07-28 06:32:19 +08:00
										 |  |  | 		if err == errFaultyDisk { | 
					
						
							| 
									
										
										
										
											2016-06-21 07:57:14 +08:00
										 |  |  | 			atomic.AddInt32(&s.ioErrCount, 1) | 
					
						
							| 
									
										
										
										
											2016-06-09 13:02:10 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-01 00:22:53 +08:00
										 |  |  | 	if atomic.LoadInt32(&s.ioErrCount) > maxAllowedIOError { | 
					
						
							| 
									
										
										
										
											2016-06-09 13:02:10 +08:00
										 |  |  | 		return VolInfo{}, errFaultyDisk | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-01 00:34:44 +08:00
										 |  |  | 	if err = s.checkDiskFound(); err != nil { | 
					
						
							| 
									
										
										
										
											2016-06-03 07:34:15 +08:00
										 |  |  | 		return VolInfo{}, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-14 02:32:47 +08:00
										 |  |  | 	// Verify if volume is valid and it exists.
 | 
					
						
							| 
									
										
										
										
											2016-05-18 12:22:27 +08:00
										 |  |  | 	volumeDir, err := s.getVolDir(volume) | 
					
						
							| 
									
										
										
										
											2016-04-14 02:32:47 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return VolInfo{}, err | 
					
						
							| 
									
										
										
										
											2016-04-09 01:37:38 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	// Stat a volume entry.
 | 
					
						
							|  |  |  | 	var st os.FileInfo | 
					
						
							| 
									
										
										
										
											2018-01-30 10:43:13 +08:00
										 |  |  | 	st, err = os.Stat(volumeDir) | 
					
						
							| 
									
										
										
										
											2016-04-09 01:37:38 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		if os.IsNotExist(err) { | 
					
						
							|  |  |  | 			return VolInfo{}, errVolumeNotFound | 
					
						
							| 
									
										
										
										
											2018-07-28 06:32:19 +08:00
										 |  |  | 		} else if isSysErrIO(err) { | 
					
						
							|  |  |  | 			return VolInfo{}, errFaultyDisk | 
					
						
							| 
									
										
										
										
											2016-04-09 01:37:38 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		return VolInfo{}, err | 
					
						
							| 
									
										
										
										
											2016-04-09 08:13:16 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-10-13 18:01:15 +08:00
										 |  |  | 	// As os.Stat() doesn't carry other than ModTime(), use ModTime()
 | 
					
						
							| 
									
										
										
										
											2016-04-17 03:48:41 +08:00
										 |  |  | 	// as CreatedTime.
 | 
					
						
							| 
									
										
										
										
											2016-04-14 02:32:47 +08:00
										 |  |  | 	createdTime := st.ModTime() | 
					
						
							| 
									
										
										
										
											2016-04-09 01:37:38 +08:00
										 |  |  | 	return VolInfo{ | 
					
						
							| 
									
										
										
										
											2016-04-14 02:32:47 +08:00
										 |  |  | 		Name:    volume, | 
					
						
							|  |  |  | 		Created: createdTime, | 
					
						
							| 
									
										
										
										
											2016-04-09 01:37:38 +08:00
										 |  |  | 	}, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // DeleteVol - delete a volume.
 | 
					
						
							| 
									
										
										
										
											2016-06-21 07:57:14 +08:00
										 |  |  | func (s *posix) DeleteVol(volume string) (err error) { | 
					
						
							| 
									
										
										
										
											2016-06-09 13:02:10 +08:00
										 |  |  | 	defer func() { | 
					
						
							| 
									
										
										
										
											2018-07-28 06:32:19 +08:00
										 |  |  | 		if err == errFaultyDisk { | 
					
						
							| 
									
										
										
										
											2016-06-21 07:57:14 +08:00
										 |  |  | 			atomic.AddInt32(&s.ioErrCount, 1) | 
					
						
							| 
									
										
										
										
											2016-06-09 13:02:10 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-01 00:22:53 +08:00
										 |  |  | 	if atomic.LoadInt32(&s.ioErrCount) > maxAllowedIOError { | 
					
						
							| 
									
										
										
										
											2016-06-09 13:02:10 +08:00
										 |  |  | 		return errFaultyDisk | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-01 00:34:44 +08:00
										 |  |  | 	if err = s.checkDiskFound(); err != nil { | 
					
						
							| 
									
										
										
										
											2016-06-03 13:49:27 +08:00
										 |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-14 02:32:47 +08:00
										 |  |  | 	// Verify if volume is valid and it exists.
 | 
					
						
							| 
									
										
										
										
											2016-05-18 12:22:27 +08:00
										 |  |  | 	volumeDir, err := s.getVolDir(volume) | 
					
						
							| 
									
										
										
										
											2016-04-14 02:32:47 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							| 
									
										
										
										
											2016-04-09 01:37:38 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-08-13 10:25:43 +08:00
										 |  |  | 	err = os.Remove((volumeDir)) | 
					
						
							| 
									
										
										
										
											2016-04-17 03:48:41 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2018-08-07 01:26:40 +08:00
										 |  |  | 		switch { | 
					
						
							|  |  |  | 		case os.IsNotExist(err): | 
					
						
							| 
									
										
										
										
											2016-04-17 03:48:41 +08:00
										 |  |  | 			return errVolumeNotFound | 
					
						
							| 
									
										
										
										
											2018-08-07 01:26:40 +08:00
										 |  |  | 		case isSysErrNotEmpty(err): | 
					
						
							| 
									
										
										
										
											2016-04-17 03:48:41 +08:00
										 |  |  | 			return errVolumeNotEmpty | 
					
						
							| 
									
										
										
										
											2018-08-07 01:26:40 +08:00
										 |  |  | 		case os.IsPermission(err): | 
					
						
							| 
									
										
										
										
											2018-04-10 11:56:09 +08:00
										 |  |  | 			return errDiskAccessDenied | 
					
						
							| 
									
										
										
										
											2018-08-07 01:26:40 +08:00
										 |  |  | 		case isSysErrIO(err): | 
					
						
							| 
									
										
										
										
											2018-07-28 06:32:19 +08:00
										 |  |  | 			return errFaultyDisk | 
					
						
							| 
									
										
										
										
											2018-08-07 01:26:40 +08:00
										 |  |  | 		default: | 
					
						
							|  |  |  | 			return err | 
					
						
							| 
									
										
										
										
											2016-04-17 03:48:41 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-04-09 01:37:38 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-03-28 12:52:38 +08:00
										 |  |  | 	return nil | 
					
						
							| 
									
										
										
										
											2016-04-09 01:37:38 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-15 04:49:10 +08:00
										 |  |  | // Walk - is a sorted walker which returns file entries in lexically
 | 
					
						
							|  |  |  | // sorted order, additionally along with metadata about each of those entries.
 | 
					
						
							|  |  |  | func (s *posix) Walk(volume, dirPath, marker string, recursive bool, leafFile string, | 
					
						
							|  |  |  | 	readMetadataFn readMetadataFunc, endWalkCh chan struct{}) (ch chan FileInfo, err error) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	defer func() { | 
					
						
							|  |  |  | 		if err == errFaultyDisk { | 
					
						
							|  |  |  | 			atomic.AddInt32(&s.ioErrCount, 1) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if atomic.LoadInt32(&s.ioErrCount) > maxAllowedIOError { | 
					
						
							|  |  |  | 		return nil, errFaultyDisk | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if err = s.checkDiskFound(); err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Verify if volume is valid and it exists.
 | 
					
						
							|  |  |  | 	volumeDir, err := s.getVolDir(volume) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Stat a volume entry.
 | 
					
						
							|  |  |  | 	_, err = os.Stat(volumeDir) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		if os.IsNotExist(err) { | 
					
						
							|  |  |  | 			return nil, errVolumeNotFound | 
					
						
							|  |  |  | 		} else if isSysErrIO(err) { | 
					
						
							|  |  |  | 			return nil, errFaultyDisk | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ch = make(chan FileInfo) | 
					
						
							|  |  |  | 	go func() { | 
					
						
							|  |  |  | 		defer close(ch) | 
					
						
							|  |  |  | 		listDir := func(volume, dirPath, dirEntry string) (entries []string) { | 
					
						
							|  |  |  | 			entries, err := s.ListDir(volume, dirPath, -1, leafFile) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			sort.Strings(entries) | 
					
						
							|  |  |  | 			return filterMatchingPrefix(entries, dirEntry) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		walkResultCh := startTreeWalk(context.Background(), volume, dirPath, marker, recursive, listDir, endWalkCh) | 
					
						
							|  |  |  | 		for { | 
					
						
							|  |  |  | 			walkResult, ok := <-walkResultCh | 
					
						
							|  |  |  | 			if !ok { | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			var fi FileInfo | 
					
						
							|  |  |  | 			if hasSuffix(walkResult.entry, slashSeparator) { | 
					
						
							|  |  |  | 				fi = FileInfo{ | 
					
						
							|  |  |  | 					Volume: volume, | 
					
						
							|  |  |  | 					Name:   walkResult.entry, | 
					
						
							|  |  |  | 					Mode:   os.ModeDir, | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				buf, err := s.ReadAll(volume, pathJoin(walkResult.entry, leafFile)) | 
					
						
							|  |  |  | 				if err != nil { | 
					
						
							|  |  |  | 					continue | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				fi = readMetadataFn(buf, volume, walkResult.entry) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			select { | 
					
						
							|  |  |  | 			case ch <- fi: | 
					
						
							|  |  |  | 			case <-endWalkCh: | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return ch, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-06 03:51:56 +08:00
										 |  |  | // ListDir - return all the entries at the given directory path.
 | 
					
						
							|  |  |  | // If an entry is a directory it will be returned with a trailing "/".
 | 
					
						
							| 
									
										
										
										
											2019-04-24 05:54:28 +08:00
										 |  |  | func (s *posix) ListDir(volume, dirPath string, count int, leafFile string) (entries []string, err error) { | 
					
						
							| 
									
										
										
										
											2016-06-09 13:02:10 +08:00
										 |  |  | 	defer func() { | 
					
						
							| 
									
										
										
										
											2018-07-28 06:32:19 +08:00
										 |  |  | 		if err == errFaultyDisk { | 
					
						
							| 
									
										
										
										
											2016-06-21 07:57:14 +08:00
										 |  |  | 			atomic.AddInt32(&s.ioErrCount, 1) | 
					
						
							| 
									
										
										
										
											2016-06-09 13:02:10 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-01 00:22:53 +08:00
										 |  |  | 	if atomic.LoadInt32(&s.ioErrCount) > maxAllowedIOError { | 
					
						
							| 
									
										
										
										
											2016-06-09 13:02:10 +08:00
										 |  |  | 		return nil, errFaultyDisk | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-01 00:34:44 +08:00
										 |  |  | 	if err = s.checkDiskFound(); err != nil { | 
					
						
							| 
									
										
										
										
											2016-06-03 13:49:27 +08:00
										 |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-14 02:32:47 +08:00
										 |  |  | 	// Verify if volume is valid and it exists.
 | 
					
						
							| 
									
										
										
										
											2016-05-18 12:22:27 +08:00
										 |  |  | 	volumeDir, err := s.getVolDir(volume) | 
					
						
							| 
									
										
										
										
											2016-04-14 02:32:47 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2016-05-06 03:51:56 +08:00
										 |  |  | 		return nil, err | 
					
						
							| 
									
										
										
										
											2016-04-09 01:37:38 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-05-06 03:51:56 +08:00
										 |  |  | 	// Stat a volume entry.
 | 
					
						
							| 
									
										
										
										
											2017-10-13 18:01:15 +08:00
										 |  |  | 	_, err = os.Stat((volumeDir)) | 
					
						
							| 
									
										
										
										
											2016-05-06 03:51:56 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		if os.IsNotExist(err) { | 
					
						
							|  |  |  | 			return nil, errVolumeNotFound | 
					
						
							| 
									
										
										
										
											2018-07-28 06:32:19 +08:00
										 |  |  | 		} else if isSysErrIO(err) { | 
					
						
							|  |  |  | 			return nil, errFaultyDisk | 
					
						
							| 
									
										
										
										
											2016-04-09 01:37:38 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-05-06 03:51:56 +08:00
										 |  |  | 		return nil, err | 
					
						
							| 
									
										
										
										
											2016-04-09 01:37:38 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-05-09 10:08:21 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	dirPath = pathJoin(volumeDir, dirPath) | 
					
						
							|  |  |  | 	if count > 0 { | 
					
						
							| 
									
										
										
										
											2019-04-24 05:54:28 +08:00
										 |  |  | 		entries, err = readDirN(dirPath, count) | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		entries, err = readDir(dirPath) | 
					
						
							| 
									
										
										
										
											2018-05-09 10:08:21 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-04-24 05:54:28 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// If leaf file is specified, filter out the entries.
 | 
					
						
							|  |  |  | 	if leafFile != "" { | 
					
						
							|  |  |  | 		for i, entry := range entries { | 
					
						
							|  |  |  | 			if _, serr := os.Stat(pathJoin(dirPath, entry, leafFile)); serr == nil { | 
					
						
							|  |  |  | 				entries[i] = strings.TrimSuffix(entry, slashSeparator) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return entries, err | 
					
						
							| 
									
										
										
										
											2016-04-09 01:37:38 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-26 05:51:06 +08:00
										 |  |  | // ReadAll reads from r until an error or EOF and returns the data it read.
 | 
					
						
							|  |  |  | // A successful call returns err == nil, not err == EOF. Because ReadAll is
 | 
					
						
							|  |  |  | // defined to read from src until EOF, it does not treat an EOF from Read
 | 
					
						
							|  |  |  | // as an error to be reported.
 | 
					
						
							|  |  |  | // This API is meant to be used on files which have small memory footprint, do
 | 
					
						
							|  |  |  | // not use this on large files as it would cause server to crash.
 | 
					
						
							|  |  |  | func (s *posix) ReadAll(volume, path string) (buf []byte, err error) { | 
					
						
							|  |  |  | 	defer func() { | 
					
						
							| 
									
										
										
										
											2018-07-28 06:32:19 +08:00
										 |  |  | 		if err == errFaultyDisk { | 
					
						
							| 
									
										
										
										
											2016-06-26 05:51:06 +08:00
										 |  |  | 			atomic.AddInt32(&s.ioErrCount, 1) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-01 00:22:53 +08:00
										 |  |  | 	if atomic.LoadInt32(&s.ioErrCount) > maxAllowedIOError { | 
					
						
							| 
									
										
										
										
											2016-06-26 05:51:06 +08:00
										 |  |  | 		return nil, errFaultyDisk | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-06-30 02:25:35 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-01 00:34:44 +08:00
										 |  |  | 	if err = s.checkDiskFound(); err != nil { | 
					
						
							| 
									
										
										
										
											2016-06-26 05:51:06 +08:00
										 |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	volumeDir, err := s.getVolDir(volume) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Stat a volume entry.
 | 
					
						
							| 
									
										
										
										
											2017-10-13 18:01:15 +08:00
										 |  |  | 	_, err = os.Stat((volumeDir)) | 
					
						
							| 
									
										
										
										
											2016-06-26 05:51:06 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		if os.IsNotExist(err) { | 
					
						
							|  |  |  | 			return nil, errVolumeNotFound | 
					
						
							| 
									
										
										
										
											2018-07-28 06:32:19 +08:00
										 |  |  | 		} else if isSysErrIO(err) { | 
					
						
							|  |  |  | 			return nil, errFaultyDisk | 
					
						
							| 
									
										
										
										
											2019-05-02 22:09:57 +08:00
										 |  |  | 		} else if isSysErrTooManyFiles(err) { | 
					
						
							|  |  |  | 			return nil, errTooManyOpenFiles | 
					
						
							| 
									
										
										
										
											2016-06-26 05:51:06 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Validate file path length, before reading.
 | 
					
						
							|  |  |  | 	filePath := pathJoin(volumeDir, path) | 
					
						
							| 
									
										
										
										
											2017-08-13 10:25:43 +08:00
										 |  |  | 	if err = checkPathLength((filePath)); err != nil { | 
					
						
							| 
									
										
										
										
											2016-06-26 05:51:06 +08:00
										 |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Open the file for reading.
 | 
					
						
							| 
									
										
										
										
											2017-08-13 10:25:43 +08:00
										 |  |  | 	buf, err = ioutil.ReadFile((filePath)) | 
					
						
							| 
									
										
										
										
											2016-06-26 05:51:06 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		if os.IsNotExist(err) { | 
					
						
							|  |  |  | 			return nil, errFileNotFound | 
					
						
							|  |  |  | 		} else if os.IsPermission(err) { | 
					
						
							|  |  |  | 			return nil, errFileAccessDenied | 
					
						
							|  |  |  | 		} else if pathErr, ok := err.(*os.PathError); ok { | 
					
						
							| 
									
										
										
										
											2016-07-11 15:15:37 +08:00
										 |  |  | 			switch pathErr.Err { | 
					
						
							|  |  |  | 			case syscall.ENOTDIR, syscall.EISDIR: | 
					
						
							| 
									
										
										
										
											2016-06-26 05:51:06 +08:00
										 |  |  | 				return nil, errFileNotFound | 
					
						
							| 
									
										
										
										
											2016-07-11 15:15:37 +08:00
										 |  |  | 			default: | 
					
						
							| 
									
										
										
										
											2016-09-01 05:43:20 +08:00
										 |  |  | 				if isSysErrHandleInvalid(pathErr.Err) { | 
					
						
							| 
									
										
										
										
											2016-07-11 15:15:37 +08:00
										 |  |  | 					// This case is special and needs to be handled for windows.
 | 
					
						
							|  |  |  | 					return nil, errFileNotFound | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2016-06-26 05:51:06 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			return nil, pathErr | 
					
						
							| 
									
										
										
										
											2018-07-28 06:32:19 +08:00
										 |  |  | 		} else if isSysErrIO(err) { | 
					
						
							|  |  |  | 			return nil, errFaultyDisk | 
					
						
							| 
									
										
										
										
											2016-06-26 05:51:06 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return buf, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-29 06:13:15 +08:00
										 |  |  | // ReadFile reads exactly len(buf) bytes into buf. It returns the
 | 
					
						
							|  |  |  | // number of bytes copied. The error is EOF only if no bytes were
 | 
					
						
							|  |  |  | // read. On return, n == len(buf) if and only if err == nil. n == 0
 | 
					
						
							| 
									
										
										
										
											2016-11-21 15:42:53 +08:00
										 |  |  | // for io.EOF.
 | 
					
						
							| 
									
										
										
										
											2017-05-17 05:21:52 +08:00
										 |  |  | //
 | 
					
						
							| 
									
										
										
										
											2016-11-21 15:42:53 +08:00
										 |  |  | // If an EOF happens after reading some but not all the bytes,
 | 
					
						
							| 
									
										
										
										
											2017-09-26 02:32:56 +08:00
										 |  |  | // ReadFile returns ErrUnexpectedEOF.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // If the BitrotVerifier is not nil or not verified ReadFile
 | 
					
						
							|  |  |  | // tries to verify whether the disk has bitrot.
 | 
					
						
							| 
									
										
										
										
											2017-05-17 05:21:52 +08:00
										 |  |  | //
 | 
					
						
							|  |  |  | // Additionally ReadFile also starts reading from an offset. ReadFile
 | 
					
						
							|  |  |  | // semantics are same as io.ReadFull.
 | 
					
						
							| 
									
										
										
										
											2018-08-07 06:14:08 +08:00
										 |  |  | func (s *posix) ReadFile(volume, path string, offset int64, buffer []byte, verifier *BitrotVerifier) (int64, error) { | 
					
						
							|  |  |  | 	var n int | 
					
						
							|  |  |  | 	var err error | 
					
						
							| 
									
										
										
										
											2016-06-09 13:02:10 +08:00
										 |  |  | 	defer func() { | 
					
						
							| 
									
										
										
										
											2018-07-28 06:32:19 +08:00
										 |  |  | 		if err == errFaultyDisk { | 
					
						
							| 
									
										
										
										
											2016-06-21 07:57:14 +08:00
										 |  |  | 			atomic.AddInt32(&s.ioErrCount, 1) | 
					
						
							| 
									
										
										
										
											2016-06-09 13:02:10 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-07 06:14:08 +08:00
										 |  |  | 	if offset < 0 { | 
					
						
							|  |  |  | 		return 0, errInvalidArgument | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-01 00:22:53 +08:00
										 |  |  | 	if atomic.LoadInt32(&s.ioErrCount) > maxAllowedIOError { | 
					
						
							| 
									
										
										
										
											2016-06-09 13:02:10 +08:00
										 |  |  | 		return 0, errFaultyDisk | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-01 00:34:44 +08:00
										 |  |  | 	if err = s.checkDiskFound(); err != nil { | 
					
						
							| 
									
										
										
										
											2016-06-03 13:49:27 +08:00
										 |  |  | 		return 0, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-18 12:22:27 +08:00
										 |  |  | 	volumeDir, err := s.getVolDir(volume) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2016-05-29 06:13:15 +08:00
										 |  |  | 		return 0, err | 
					
						
							| 
									
										
										
										
											2016-05-18 12:22:27 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	// Stat a volume entry.
 | 
					
						
							| 
									
										
										
										
											2017-10-13 18:01:15 +08:00
										 |  |  | 	_, err = os.Stat((volumeDir)) | 
					
						
							| 
									
										
										
										
											2016-04-14 02:32:47 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2016-05-18 12:22:27 +08:00
										 |  |  | 		if os.IsNotExist(err) { | 
					
						
							| 
									
										
										
										
											2016-05-29 06:13:15 +08:00
										 |  |  | 			return 0, errVolumeNotFound | 
					
						
							| 
									
										
										
										
											2018-07-28 06:32:19 +08:00
										 |  |  | 		} else if isSysErrIO(err) { | 
					
						
							|  |  |  | 			return 0, errFaultyDisk | 
					
						
							| 
									
										
										
										
											2016-05-18 12:22:27 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-05-29 06:13:15 +08:00
										 |  |  | 		return 0, err | 
					
						
							| 
									
										
										
										
											2016-04-09 01:37:38 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-04-14 02:32:47 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-26 05:51:06 +08:00
										 |  |  | 	// Validate effective path length before reading.
 | 
					
						
							| 
									
										
										
										
											2016-05-05 16:39:26 +08:00
										 |  |  | 	filePath := pathJoin(volumeDir, path) | 
					
						
							| 
									
										
										
										
											2017-08-13 10:25:43 +08:00
										 |  |  | 	if err = checkPathLength((filePath)); err != nil { | 
					
						
							| 
									
										
										
										
											2016-05-29 06:13:15 +08:00
										 |  |  | 		return 0, err | 
					
						
							| 
									
										
										
										
											2016-05-12 03:55:02 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-06-26 05:51:06 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Open the file for reading.
 | 
					
						
							| 
									
										
										
										
											2017-08-13 10:25:43 +08:00
										 |  |  | 	file, err := os.Open((filePath)) | 
					
						
							| 
									
										
										
										
											2016-04-09 01:37:38 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2018-08-07 01:26:40 +08:00
										 |  |  | 		switch { | 
					
						
							|  |  |  | 		case os.IsNotExist(err): | 
					
						
							| 
									
										
										
										
											2016-05-29 06:13:15 +08:00
										 |  |  | 			return 0, errFileNotFound | 
					
						
							| 
									
										
										
										
											2018-08-07 01:26:40 +08:00
										 |  |  | 		case os.IsPermission(err): | 
					
						
							| 
									
										
										
										
											2016-05-29 06:13:15 +08:00
										 |  |  | 			return 0, errFileAccessDenied | 
					
						
							| 
									
										
										
										
											2018-08-07 01:26:40 +08:00
										 |  |  | 		case isSysErrNotDir(err): | 
					
						
							| 
									
										
										
										
											2016-07-29 12:57:11 +08:00
										 |  |  | 			return 0, errFileAccessDenied | 
					
						
							| 
									
										
										
										
											2018-08-07 01:26:40 +08:00
										 |  |  | 		case isSysErrIO(err): | 
					
						
							| 
									
										
										
										
											2018-07-28 06:32:19 +08:00
										 |  |  | 			return 0, errFaultyDisk | 
					
						
							| 
									
										
										
										
											2019-05-02 22:09:57 +08:00
										 |  |  | 		case isSysErrTooManyFiles(err): | 
					
						
							|  |  |  | 			return 0, errTooManyOpenFiles | 
					
						
							| 
									
										
										
										
											2018-08-07 01:26:40 +08:00
										 |  |  | 		default: | 
					
						
							|  |  |  | 			return 0, err | 
					
						
							| 
									
										
										
										
											2016-04-09 01:37:38 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-07-28 10:22:32 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Close the file descriptor.
 | 
					
						
							|  |  |  | 	defer file.Close() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-09 01:37:38 +08:00
										 |  |  | 	st, err := file.Stat() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2016-05-29 06:13:15 +08:00
										 |  |  | 		return 0, err | 
					
						
							| 
									
										
										
										
											2016-04-09 01:37:38 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-07-29 12:57:11 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-17 05:21:52 +08:00
										 |  |  | 	// Verify it is a regular file, otherwise subsequent Seek is
 | 
					
						
							|  |  |  | 	// undefined.
 | 
					
						
							| 
									
										
										
										
											2016-04-09 01:37:38 +08:00
										 |  |  | 	if !st.Mode().IsRegular() { | 
					
						
							| 
									
										
										
										
											2016-07-29 12:57:11 +08:00
										 |  |  | 		return 0, errIsNotRegular | 
					
						
							| 
									
										
										
										
											2016-04-09 01:37:38 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-07-29 12:57:11 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-07 06:14:08 +08:00
										 |  |  | 	if verifier == nil { | 
					
						
							|  |  |  | 		n, err = file.ReadAt(buffer, offset) | 
					
						
							|  |  |  | 		return int64(n), err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-05-17 05:21:52 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-07 06:14:08 +08:00
										 |  |  | 	bufp := s.pool.Get().(*[]byte) | 
					
						
							|  |  |  | 	defer s.pool.Put(bufp) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	h := verifier.algorithm.New() | 
					
						
							|  |  |  | 	if _, err = io.CopyBuffer(h, io.LimitReader(file, offset), *bufp); err != nil { | 
					
						
							|  |  |  | 		return 0, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if n, err = io.ReadFull(file, buffer); err != nil { | 
					
						
							|  |  |  | 		return int64(n), err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if _, err = h.Write(buffer); err != nil { | 
					
						
							|  |  |  | 		return 0, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if _, err = io.CopyBuffer(h, file, *bufp); err != nil { | 
					
						
							|  |  |  | 		return 0, err | 
					
						
							| 
									
										
										
										
											2017-05-17 05:21:52 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-05-29 06:13:15 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-13 20:59:36 +08:00
										 |  |  | 	if !bytes.Equal(h.Sum(nil), verifier.sum) { | 
					
						
							| 
									
										
										
										
											2018-08-07 06:14:08 +08:00
										 |  |  | 		return 0, hashMismatchError{hex.EncodeToString(verifier.sum), hex.EncodeToString(h.Sum(nil))} | 
					
						
							| 
									
										
										
										
											2017-08-15 09:08:42 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-08-07 06:14:08 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return int64(len(buffer)), nil | 
					
						
							| 
									
										
										
										
											2016-04-09 08:13:16 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-14 22:18:35 +08:00
										 |  |  | func (s *posix) openFile(volume, path string, mode int) (f *os.File, err error) { | 
					
						
							| 
									
										
										
										
											2016-06-09 13:02:10 +08:00
										 |  |  | 	defer func() { | 
					
						
							| 
									
										
										
										
											2018-07-28 06:32:19 +08:00
										 |  |  | 		if err == errFaultyDisk { | 
					
						
							| 
									
										
										
										
											2016-06-21 07:57:14 +08:00
										 |  |  | 			atomic.AddInt32(&s.ioErrCount, 1) | 
					
						
							| 
									
										
										
										
											2016-06-09 13:02:10 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-01 00:22:53 +08:00
										 |  |  | 	if atomic.LoadInt32(&s.ioErrCount) > maxAllowedIOError { | 
					
						
							| 
									
										
										
										
											2016-10-30 03:44:44 +08:00
										 |  |  | 		return nil, errFaultyDisk | 
					
						
							| 
									
										
										
										
											2016-06-09 13:02:10 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-01 00:34:44 +08:00
										 |  |  | 	if err = s.checkDiskFound(); err != nil { | 
					
						
							| 
									
										
										
										
											2016-10-30 03:44:44 +08:00
										 |  |  | 		return nil, err | 
					
						
							| 
									
										
										
										
											2016-06-03 13:49:27 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-18 12:22:27 +08:00
										 |  |  | 	volumeDir, err := s.getVolDir(volume) | 
					
						
							| 
									
										
										
										
											2016-04-14 02:32:47 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2016-10-30 03:44:44 +08:00
										 |  |  | 		return nil, err | 
					
						
							| 
									
										
										
										
											2016-04-09 01:37:38 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-05-18 12:22:27 +08:00
										 |  |  | 	// Stat a volume entry.
 | 
					
						
							| 
									
										
										
										
											2017-10-13 18:01:15 +08:00
										 |  |  | 	_, err = os.Stat((volumeDir)) | 
					
						
							| 
									
										
										
										
											2016-05-18 12:22:27 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		if os.IsNotExist(err) { | 
					
						
							| 
									
										
										
										
											2016-10-30 03:44:44 +08:00
										 |  |  | 			return nil, errVolumeNotFound | 
					
						
							| 
									
										
										
										
											2018-07-28 06:32:19 +08:00
										 |  |  | 		} else if isSysErrIO(err) { | 
					
						
							|  |  |  | 			return nil, errFaultyDisk | 
					
						
							| 
									
										
										
										
											2016-05-18 12:22:27 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-10-30 03:44:44 +08:00
										 |  |  | 		return nil, err | 
					
						
							| 
									
										
										
										
											2016-05-18 12:22:27 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-10-30 03:44:44 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-05 16:39:26 +08:00
										 |  |  | 	filePath := pathJoin(volumeDir, path) | 
					
						
							| 
									
										
										
										
											2017-08-13 10:25:43 +08:00
										 |  |  | 	if err = checkPathLength((filePath)); err != nil { | 
					
						
							| 
									
										
										
										
											2016-10-30 03:44:44 +08:00
										 |  |  | 		return nil, err | 
					
						
							| 
									
										
										
										
											2016-05-12 03:55:02 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-10-30 03:44:44 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-09 01:37:38 +08:00
										 |  |  | 	// Verify if the file already exists and is not of regular type.
 | 
					
						
							| 
									
										
										
										
											2016-05-05 03:18:40 +08:00
										 |  |  | 	var st os.FileInfo | 
					
						
							| 
									
										
										
										
											2018-04-10 11:56:09 +08:00
										 |  |  | 	if st, err = os.Stat(filePath); err == nil { | 
					
						
							| 
									
										
										
										
											2016-07-29 12:57:11 +08:00
										 |  |  | 		if !st.Mode().IsRegular() { | 
					
						
							| 
									
										
										
										
											2016-10-30 03:44:44 +08:00
										 |  |  | 			return nil, errIsNotRegular | 
					
						
							| 
									
										
										
										
											2016-04-09 01:37:38 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-10-30 03:44:44 +08:00
										 |  |  | 	} else { | 
					
						
							|  |  |  | 		// Create top level directories if they don't exist.
 | 
					
						
							|  |  |  | 		// with mode 0777 mkdir honors system umask.
 | 
					
						
							| 
									
										
										
										
											2018-01-14 01:13:02 +08:00
										 |  |  | 		if err = mkdirAll(slashpath.Dir(filePath), 0777); err != nil { | 
					
						
							| 
									
										
										
										
											2016-10-30 03:44:44 +08:00
										 |  |  | 			return nil, err | 
					
						
							| 
									
										
										
										
											2016-07-04 02:17:08 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-05-29 06:13:15 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-06-24 11:19:27 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-14 22:18:35 +08:00
										 |  |  | 	w, err := os.OpenFile(filePath, mode, 0666) | 
					
						
							| 
									
										
										
										
											2016-05-05 03:18:40 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		// File path cannot be verified since one of the parents is a file.
 | 
					
						
							| 
									
										
										
										
											2018-08-07 01:26:40 +08:00
										 |  |  | 		switch { | 
					
						
							|  |  |  | 		case isSysErrNotDir(err): | 
					
						
							| 
									
										
										
										
											2016-10-30 03:44:44 +08:00
										 |  |  | 			return nil, errFileAccessDenied | 
					
						
							| 
									
										
										
										
											2018-08-07 01:26:40 +08:00
										 |  |  | 		case os.IsPermission(err): | 
					
						
							| 
									
										
										
										
											2018-04-10 11:56:09 +08:00
										 |  |  | 			return nil, errFileAccessDenied | 
					
						
							| 
									
										
										
										
											2018-08-07 01:26:40 +08:00
										 |  |  | 		case isSysErrIO(err): | 
					
						
							| 
									
										
										
										
											2018-07-28 06:32:19 +08:00
										 |  |  | 			return nil, errFaultyDisk | 
					
						
							| 
									
										
										
										
											2019-05-02 22:09:57 +08:00
										 |  |  | 		case isSysErrTooManyFiles(err): | 
					
						
							|  |  |  | 			return nil, errTooManyOpenFiles | 
					
						
							| 
									
										
										
										
											2018-08-07 01:26:40 +08:00
										 |  |  | 		default: | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							| 
									
										
										
										
											2016-05-05 03:18:40 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-10-30 03:44:44 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return w, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | // ReadFileStream - Returns the read stream of the file.
 | 
					
						
							|  |  |  | func (s *posix) ReadFileStream(volume, path string, offset, length int64) (io.ReadCloser, error) { | 
					
						
							|  |  |  | 	var err error | 
					
						
							|  |  |  | 	defer func() { | 
					
						
							|  |  |  | 		if err == errFaultyDisk { | 
					
						
							|  |  |  | 			atomic.AddInt32(&s.ioErrCount, 1) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if offset < 0 { | 
					
						
							|  |  |  | 		return nil, errInvalidArgument | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if atomic.LoadInt32(&s.ioErrCount) > maxAllowedIOError { | 
					
						
							|  |  |  | 		return nil, errFaultyDisk | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if err = s.checkDiskFound(); err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	volumeDir, err := s.getVolDir(volume) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Stat a volume entry.
 | 
					
						
							|  |  |  | 	_, err = os.Stat((volumeDir)) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		if os.IsNotExist(err) { | 
					
						
							|  |  |  | 			return nil, errVolumeNotFound | 
					
						
							|  |  |  | 		} else if isSysErrIO(err) { | 
					
						
							|  |  |  | 			return nil, errFaultyDisk | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Validate effective path length before reading.
 | 
					
						
							|  |  |  | 	filePath := pathJoin(volumeDir, path) | 
					
						
							|  |  |  | 	if err = checkPathLength((filePath)); err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Open the file for reading.
 | 
					
						
							|  |  |  | 	file, err := os.Open((filePath)) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		switch { | 
					
						
							|  |  |  | 		case os.IsNotExist(err): | 
					
						
							|  |  |  | 			return nil, errFileNotFound | 
					
						
							|  |  |  | 		case os.IsPermission(err): | 
					
						
							|  |  |  | 			return nil, errFileAccessDenied | 
					
						
							|  |  |  | 		case isSysErrNotDir(err): | 
					
						
							|  |  |  | 			return nil, errFileAccessDenied | 
					
						
							|  |  |  | 		case isSysErrIO(err): | 
					
						
							|  |  |  | 			return nil, errFaultyDisk | 
					
						
							| 
									
										
										
										
											2019-05-02 22:09:57 +08:00
										 |  |  | 		case isSysErrTooManyFiles(err): | 
					
						
							|  |  |  | 			return nil, errTooManyOpenFiles | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | 		default: | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	st, err := file.Stat() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Verify it is a regular file, otherwise subsequent Seek is
 | 
					
						
							|  |  |  | 	// undefined.
 | 
					
						
							|  |  |  | 	if !st.Mode().IsRegular() { | 
					
						
							|  |  |  | 		return nil, errIsNotRegular | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if _, err = file.Seek(offset, io.SeekStart); err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-05-23 04:47:15 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	r := struct { | 
					
						
							| 
									
										
										
										
											2019-03-18 13:20:26 +08:00
										 |  |  | 		io.Reader | 
					
						
							|  |  |  | 		io.Closer | 
					
						
							| 
									
										
										
										
											2019-05-23 04:47:15 +08:00
										 |  |  | 	}{Reader: io.LimitReader(file, length), Closer: file} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return readahead.NewReadCloser(r), nil | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // CreateFile - creates the file.
 | 
					
						
							|  |  |  | func (s *posix) CreateFile(volume, path string, fileSize int64, r io.Reader) (err error) { | 
					
						
							| 
									
										
										
										
											2019-05-01 07:27:31 +08:00
										 |  |  | 	if fileSize < -1 { | 
					
						
							| 
									
										
										
										
											2016-10-30 03:44:44 +08:00
										 |  |  | 		return errInvalidArgument | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	defer func() { | 
					
						
							| 
									
										
										
										
											2018-07-28 06:32:19 +08:00
										 |  |  | 		if err == errFaultyDisk { | 
					
						
							| 
									
										
										
										
											2016-10-30 03:44:44 +08:00
										 |  |  | 			atomic.AddInt32(&s.ioErrCount, 1) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-01 00:22:53 +08:00
										 |  |  | 	if atomic.LoadInt32(&s.ioErrCount) > maxAllowedIOError { | 
					
						
							| 
									
										
										
										
											2016-10-30 03:44:44 +08:00
										 |  |  | 		return errFaultyDisk | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-01 00:34:44 +08:00
										 |  |  | 	// Validate if disk is indeed free.
 | 
					
						
							| 
									
										
										
										
											2017-03-08 04:25:40 +08:00
										 |  |  | 	if err = checkDiskFree(s.diskPath, fileSize); err != nil { | 
					
						
							| 
									
										
										
										
											2018-07-28 06:32:19 +08:00
										 |  |  | 		if isSysErrIO(err) { | 
					
						
							|  |  |  | 			return errFaultyDisk | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-11-01 00:34:44 +08:00
										 |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-24 12:25:06 +08:00
										 |  |  | 	if err = s.checkDiskFound(); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	volumeDir, err := s.getVolDir(volume) | 
					
						
							| 
									
										
										
										
											2016-10-30 03:44:44 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-04-24 12:25:06 +08:00
										 |  |  | 	// Stat a volume entry.
 | 
					
						
							|  |  |  | 	_, err = os.Stat((volumeDir)) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		if os.IsNotExist(err) { | 
					
						
							|  |  |  | 			return errVolumeNotFound | 
					
						
							|  |  |  | 		} else if isSysErrIO(err) { | 
					
						
							|  |  |  | 			return errFaultyDisk | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	filePath := pathJoin(volumeDir, path) | 
					
						
							|  |  |  | 	if err = checkPathLength((filePath)); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-10-30 03:44:44 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-24 12:25:06 +08:00
										 |  |  | 	// 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 { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-12 06:03:10 +08:00
										 |  |  | 	w, err := disk.OpenFileDirectIO(filePath, os.O_CREATE|os.O_WRONLY|os.O_EXCL, 0666) | 
					
						
							| 
									
										
										
										
											2019-04-24 12:25:06 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		switch { | 
					
						
							|  |  |  | 		case os.IsPermission(err): | 
					
						
							|  |  |  | 			return errFileAccessDenied | 
					
						
							|  |  |  | 		case os.IsExist(err): | 
					
						
							|  |  |  | 			return errFileAccessDenied | 
					
						
							|  |  |  | 		case isSysErrIO(err): | 
					
						
							|  |  |  | 			return errFaultyDisk | 
					
						
							|  |  |  | 		default: | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-10-30 03:44:44 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-28 11:36:17 +08:00
										 |  |  | 	var e error | 
					
						
							|  |  |  | 	if fileSize > 0 { | 
					
						
							|  |  |  | 		// Allocate needed disk space to append data
 | 
					
						
							|  |  |  | 		e = Fallocate(int(w.Fd()), 0, fileSize) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-10-30 03:44:44 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Ignore errors when Fallocate is not supported in the current system
 | 
					
						
							|  |  |  | 	if e != nil && !isSysErrNoSys(e) && !isSysErrOpNotSupported(e) { | 
					
						
							|  |  |  | 		switch { | 
					
						
							|  |  |  | 		case isSysErrNoSpace(e): | 
					
						
							|  |  |  | 			err = errDiskFull | 
					
						
							|  |  |  | 		case isSysErrIO(e): | 
					
						
							| 
									
										
										
										
											2018-07-28 06:32:19 +08:00
										 |  |  | 			err = errFaultyDisk | 
					
						
							| 
									
										
										
										
											2016-10-30 03:44:44 +08:00
										 |  |  | 		default: | 
					
						
							|  |  |  | 			// For errors: EBADF, EINTR, EINVAL, ENODEV, EPERM, ESPIPE  and ETXTBSY
 | 
					
						
							|  |  |  | 			// Appending was failed anyway, returns unexpected error
 | 
					
						
							|  |  |  | 			err = errUnexpected | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-23 04:47:15 +08:00
										 |  |  | 	defer w.Close() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | 	bufp := s.pool.Get().(*[]byte) | 
					
						
							|  |  |  | 	defer s.pool.Put(bufp) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-23 04:47:15 +08:00
										 |  |  | 	written, err := xioutil.CopyAligned(w, r, *bufp) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-04-24 12:25:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-23 04:47:15 +08:00
										 |  |  | 	if written < fileSize { | 
					
						
							|  |  |  | 		return errLessData | 
					
						
							|  |  |  | 	} else if written > fileSize { | 
					
						
							|  |  |  | 		return errMoreData | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-05-23 04:47:15 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return nil | 
					
						
							| 
									
										
										
										
											2016-10-30 03:44:44 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-23 04:47:15 +08:00
										 |  |  | func (s *posix) WriteAll(volume, path string, reader io.Reader) (err error) { | 
					
						
							| 
									
										
										
										
											2018-11-14 22:18:35 +08:00
										 |  |  | 	defer func() { | 
					
						
							|  |  |  | 		if err == errFaultyDisk { | 
					
						
							|  |  |  | 			atomic.AddInt32(&s.ioErrCount, 1) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if atomic.LoadInt32(&s.ioErrCount) > maxAllowedIOError { | 
					
						
							|  |  |  | 		return errFaultyDisk | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-02 11:01:06 +08:00
										 |  |  | 	// Create file if not found. Note that it is created with os.O_EXCL flag as the file
 | 
					
						
							|  |  |  | 	// always is supposed to be created in the tmp directory with a unique file name.
 | 
					
						
							|  |  |  | 	w, err := s.openFile(volume, path, os.O_CREATE|os.O_SYNC|os.O_WRONLY|os.O_EXCL) | 
					
						
							| 
									
										
										
										
											2018-11-14 22:18:35 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-23 04:47:15 +08:00
										 |  |  | 	defer w.Close() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	bufp := s.pool.Get().(*[]byte) | 
					
						
							|  |  |  | 	defer s.pool.Put(bufp) | 
					
						
							| 
									
										
										
										
											2018-11-14 22:18:35 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-23 04:47:15 +08:00
										 |  |  | 	_, err = io.CopyBuffer(w, reader, *bufp) | 
					
						
							|  |  |  | 	return err | 
					
						
							| 
									
										
										
										
											2018-11-14 22:18:35 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-30 03:44:44 +08:00
										 |  |  | // AppendFile - append a byte array at path, if file doesn't exist at
 | 
					
						
							|  |  |  | // path this call explicitly creates it.
 | 
					
						
							|  |  |  | func (s *posix) AppendFile(volume, path string, buf []byte) (err error) { | 
					
						
							|  |  |  | 	defer func() { | 
					
						
							| 
									
										
										
										
											2018-07-28 06:32:19 +08:00
										 |  |  | 		if err == errFaultyDisk { | 
					
						
							| 
									
										
										
										
											2016-10-30 03:44:44 +08:00
										 |  |  | 			atomic.AddInt32(&s.ioErrCount, 1) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-01 00:22:53 +08:00
										 |  |  | 	if atomic.LoadInt32(&s.ioErrCount) > maxAllowedIOError { | 
					
						
							| 
									
										
										
										
											2016-10-30 03:44:44 +08:00
										 |  |  | 		return errFaultyDisk | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-12 08:22:56 +08:00
										 |  |  | 	var w *os.File | 
					
						
							| 
									
										
										
										
											2019-04-24 12:25:06 +08:00
										 |  |  | 	// Create file if not found. Not doing O_DIRECT here to avoid the code that does buffer aligned writes.
 | 
					
						
							|  |  |  | 	// AppendFile() is only used by healing code to heal objects written in old format.
 | 
					
						
							|  |  |  | 	w, err = s.openFile(volume, path, os.O_CREATE|os.O_SYNC|os.O_APPEND|os.O_WRONLY) | 
					
						
							| 
									
										
										
										
											2016-10-30 03:44:44 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2016-06-20 06:31:13 +08:00
										 |  |  | 		return err | 
					
						
							| 
									
										
										
										
											2016-05-05 03:18:40 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-11-14 22:18:35 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if _, err = w.Write(buf); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return w.Close() | 
					
						
							| 
									
										
										
										
											2016-04-09 01:37:38 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // StatFile - get file info.
 | 
					
						
							| 
									
										
										
										
											2016-06-21 07:57:14 +08:00
										 |  |  | func (s *posix) StatFile(volume, path string) (file FileInfo, err error) { | 
					
						
							| 
									
										
										
										
											2016-06-09 13:02:10 +08:00
										 |  |  | 	defer func() { | 
					
						
							| 
									
										
										
										
											2018-07-28 06:32:19 +08:00
										 |  |  | 		if err == errFaultyDisk { | 
					
						
							| 
									
										
										
										
											2016-06-21 07:57:14 +08:00
										 |  |  | 			atomic.AddInt32(&s.ioErrCount, 1) | 
					
						
							| 
									
										
										
										
											2016-06-09 13:02:10 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-01 00:22:53 +08:00
										 |  |  | 	if atomic.LoadInt32(&s.ioErrCount) > maxAllowedIOError { | 
					
						
							| 
									
										
										
										
											2016-06-09 13:02:10 +08:00
										 |  |  | 		return FileInfo{}, errFaultyDisk | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-01 00:34:44 +08:00
										 |  |  | 	if err = s.checkDiskFound(); err != nil { | 
					
						
							| 
									
										
										
										
											2016-06-03 13:49:27 +08:00
										 |  |  | 		return FileInfo{}, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-18 12:22:27 +08:00
										 |  |  | 	volumeDir, err := s.getVolDir(volume) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return FileInfo{}, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Stat a volume entry.
 | 
					
						
							| 
									
										
										
										
											2017-10-13 18:01:15 +08:00
										 |  |  | 	_, err = os.Stat((volumeDir)) | 
					
						
							| 
									
										
										
										
											2016-04-14 02:32:47 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2016-05-18 12:22:27 +08:00
										 |  |  | 		if os.IsNotExist(err) { | 
					
						
							|  |  |  | 			return FileInfo{}, errVolumeNotFound | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-04-14 02:32:47 +08:00
										 |  |  | 		return FileInfo{}, err | 
					
						
							| 
									
										
										
										
											2016-04-09 01:37:38 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-09 15:46:54 +08:00
										 |  |  | 	filePath := slashpath.Join(volumeDir, path) | 
					
						
							| 
									
										
										
										
											2017-08-13 10:25:43 +08:00
										 |  |  | 	if err = checkPathLength((filePath)); err != nil { | 
					
						
							| 
									
										
										
										
											2016-05-12 03:55:02 +08:00
										 |  |  | 		return FileInfo{}, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-10-13 18:01:15 +08:00
										 |  |  | 	st, err := os.Stat((filePath)) | 
					
						
							| 
									
										
										
										
											2016-04-09 01:37:38 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2019-03-18 22:46:20 +08:00
										 |  |  | 		switch { | 
					
						
							|  |  |  | 		case os.IsNotExist(err): | 
					
						
							|  |  |  | 			// File is really not found.
 | 
					
						
							| 
									
										
										
										
											2016-04-09 01:37:38 +08:00
										 |  |  | 			return FileInfo{}, errFileNotFound | 
					
						
							| 
									
										
										
										
											2019-03-18 22:46:20 +08:00
										 |  |  | 		case isSysErrIO(err): | 
					
						
							| 
									
										
										
										
											2018-07-28 06:32:19 +08:00
										 |  |  | 			return FileInfo{}, errFaultyDisk | 
					
						
							| 
									
										
										
										
											2019-03-18 22:46:20 +08:00
										 |  |  | 		case isSysErrNotDir(err): | 
					
						
							| 
									
										
										
										
											2018-07-28 06:32:19 +08:00
										 |  |  | 			// File path cannot be verified since one of the parents is a file.
 | 
					
						
							| 
									
										
										
										
											2016-04-26 01:39:28 +08:00
										 |  |  | 			return FileInfo{}, errFileNotFound | 
					
						
							| 
									
										
										
										
											2019-03-18 22:46:20 +08:00
										 |  |  | 		default: | 
					
						
							|  |  |  | 			// Return all errors here.
 | 
					
						
							|  |  |  | 			return FileInfo{}, err | 
					
						
							| 
									
										
										
										
											2016-04-13 03:45:15 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-04-09 01:37:38 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-04-14 02:32:47 +08:00
										 |  |  | 	// If its a directory its not a regular file.
 | 
					
						
							| 
									
										
										
										
											2016-04-09 01:37:38 +08:00
										 |  |  | 	if st.Mode().IsDir() { | 
					
						
							| 
									
										
										
										
											2016-04-26 01:39:28 +08:00
										 |  |  | 		return FileInfo{}, errFileNotFound | 
					
						
							| 
									
										
										
										
											2016-04-09 01:37:38 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-04-24 15:36:00 +08:00
										 |  |  | 	return FileInfo{ | 
					
						
							| 
									
										
										
										
											2016-04-09 01:37:38 +08:00
										 |  |  | 		Volume:  volume, | 
					
						
							|  |  |  | 		Name:    path, | 
					
						
							|  |  |  | 		ModTime: st.ModTime(), | 
					
						
							|  |  |  | 		Size:    st.Size(), | 
					
						
							|  |  |  | 		Mode:    st.Mode(), | 
					
						
							| 
									
										
										
										
											2016-04-24 15:36:00 +08:00
										 |  |  | 	}, nil | 
					
						
							| 
									
										
										
										
											2016-04-09 01:37:38 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-04 11:04:28 +08:00
										 |  |  | // deleteFile 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.
 | 
					
						
							| 
									
										
										
										
											2016-04-14 02:32:47 +08:00
										 |  |  | func deleteFile(basePath, deletePath string) error { | 
					
						
							| 
									
										
										
										
											2016-04-09 01:37:38 +08:00
										 |  |  | 	if basePath == deletePath { | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-08-04 11:04:28 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-09 01:37:38 +08:00
										 |  |  | 	// Attempt to remove path.
 | 
					
						
							| 
									
										
										
										
											2017-08-13 10:25:43 +08:00
										 |  |  | 	if err := os.Remove((deletePath)); err != nil { | 
					
						
							| 
									
										
										
										
											2019-03-18 22:46:20 +08:00
										 |  |  | 		switch { | 
					
						
							|  |  |  | 		case isSysErrNotEmpty(err): | 
					
						
							|  |  |  | 			// 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.
 | 
					
						
							| 
									
										
										
										
											2017-08-04 11:04:28 +08:00
										 |  |  | 			return nil | 
					
						
							| 
									
										
										
										
											2019-03-18 22:46:20 +08:00
										 |  |  | 		case os.IsNotExist(err): | 
					
						
							| 
									
										
										
										
											2016-10-18 07:38:46 +08:00
										 |  |  | 			return errFileNotFound | 
					
						
							| 
									
										
										
										
											2019-03-18 22:46:20 +08:00
										 |  |  | 		case os.IsPermission(err): | 
					
						
							| 
									
										
										
										
											2016-10-18 07:38:46 +08:00
										 |  |  | 			return errFileAccessDenied | 
					
						
							| 
									
										
										
										
											2019-03-18 22:46:20 +08:00
										 |  |  | 		case isSysErrIO(err): | 
					
						
							| 
									
										
										
										
											2018-07-28 06:32:19 +08:00
										 |  |  | 			return errFaultyDisk | 
					
						
							| 
									
										
										
										
											2019-03-18 22:46:20 +08:00
										 |  |  | 		default: | 
					
						
							|  |  |  | 			return err | 
					
						
							| 
									
										
										
										
											2016-10-18 07:38:46 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-04-09 01:37:38 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-08-04 11:04:28 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-21 07:33:26 +08:00
										 |  |  | 	// Trailing slash is removed when found to ensure
 | 
					
						
							|  |  |  | 	// slashpath.Dir() to work as intended.
 | 
					
						
							|  |  |  | 	deletePath = strings.TrimSuffix(deletePath, slashSeparator) | 
					
						
							|  |  |  | 	deletePath = slashpath.Dir(deletePath) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Delete parent directory. Errors for parent directories shouldn't trickle down.
 | 
					
						
							|  |  |  | 	deleteFile(basePath, deletePath) | 
					
						
							| 
									
										
											  
											
												posix: do not upstream errors in deleteFile (#4771)
This commit changes posix's deleteFile() to not upstream errors from
removing parent directories. This fixes a race condition.
The race condition occurs when multiple deleteFile()s are called on the
same parent directory, but different child files. Because deleteFile()
recursively removes parent directories if they are empty, but
deleteFile() errors if the selected deletePath does not exist, there was
an opportunity for a race condition. The two processes would remove the
child directories successfully, then depend on the parent directory
still existing. In some cases this is an invalid assumption, because
other processes can remove the parent directory beforehand. This commit
changes deleteFile() to not upstream an error if one occurs, because the
only required error should be from the immediate deletePath, not from a
parent path.
In the specific bug report, multiple CompleteMultipartUpload requests
would launch multiple deleteFile() requests. Because they chain up on
parent directories, ultimately at the end, there would be multiple
remove files for the ultimate parent directory,
.minio.sys/multipart/{bucket}. Because only one will succeed and one
will fail, an error would be upstreamed saying that the file does not
exist, and the CompleteMultipartUpload code interpreted this as
NoSuchKey, or that the object/part id doesn't exist. This was faulty
behavior and is now fixed.
The added test fails before this change and passes after this change.
Fixes: https://github.com/minio/minio/issues/4727
											
										 
											2017-08-05 07:51:20 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return nil | 
					
						
							| 
									
										
										
										
											2016-04-09 08:13:16 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-09 01:37:38 +08:00
										 |  |  | // DeleteFile - delete a file at path.
 | 
					
						
							| 
									
										
										
										
											2016-06-21 07:57:14 +08:00
										 |  |  | func (s *posix) DeleteFile(volume, path string) (err error) { | 
					
						
							| 
									
										
										
										
											2016-06-09 13:02:10 +08:00
										 |  |  | 	defer func() { | 
					
						
							| 
									
										
										
										
											2018-07-28 06:32:19 +08:00
										 |  |  | 		if err == errFaultyDisk { | 
					
						
							| 
									
										
										
										
											2016-06-21 07:57:14 +08:00
										 |  |  | 			atomic.AddInt32(&s.ioErrCount, 1) | 
					
						
							| 
									
										
										
										
											2016-06-09 13:02:10 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-01 00:22:53 +08:00
										 |  |  | 	if atomic.LoadInt32(&s.ioErrCount) > maxAllowedIOError { | 
					
						
							| 
									
										
										
										
											2016-06-09 13:02:10 +08:00
										 |  |  | 		return errFaultyDisk | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-01 00:34:44 +08:00
										 |  |  | 	if err = s.checkDiskFound(); err != nil { | 
					
						
							| 
									
										
										
										
											2016-06-03 13:49:27 +08:00
										 |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-18 12:22:27 +08:00
										 |  |  | 	volumeDir, err := s.getVolDir(volume) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Stat a volume entry.
 | 
					
						
							| 
									
										
										
										
											2017-10-13 18:01:15 +08:00
										 |  |  | 	_, err = os.Stat((volumeDir)) | 
					
						
							| 
									
										
										
										
											2016-04-14 02:32:47 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2016-05-18 12:22:27 +08:00
										 |  |  | 		if os.IsNotExist(err) { | 
					
						
							|  |  |  | 			return errVolumeNotFound | 
					
						
							| 
									
										
										
										
											2018-07-28 06:32:19 +08:00
										 |  |  | 		} else if isSysErrIO(err) { | 
					
						
							|  |  |  | 			return errFaultyDisk | 
					
						
							| 
									
										
										
										
											2016-05-18 12:22:27 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-04-14 02:32:47 +08:00
										 |  |  | 		return err | 
					
						
							| 
									
										
										
										
											2016-04-09 01:37:38 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Following code is needed so that we retain "/" suffix if any in
 | 
					
						
							| 
									
										
										
										
											2016-04-14 02:32:47 +08:00
										 |  |  | 	// path argument.
 | 
					
						
							| 
									
										
										
										
											2016-05-05 16:39:26 +08:00
										 |  |  | 	filePath := pathJoin(volumeDir, path) | 
					
						
							| 
									
										
										
										
											2017-08-13 10:25:43 +08:00
										 |  |  | 	if err = checkPathLength((filePath)); err != nil { | 
					
						
							| 
									
										
										
										
											2016-05-12 03:55:02 +08:00
										 |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-04-09 01:37:38 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Delete file and delete parent directory as well if its empty.
 | 
					
						
							| 
									
										
										
										
											2016-04-14 02:32:47 +08:00
										 |  |  | 	return deleteFile(volumeDir, filePath) | 
					
						
							| 
									
										
											  
											
												config/main: Re-write config files - add to new config v3
- New config format.
```
{
	"version": "3",
	"address": ":9000",
    "backend": {
          "type": "fs",
          "disk": "/path"
    },
	"credential": {
		"accessKey": "WLGDGYAQYIGI833EV05A",
		"secretKey": "BYvgJM101sHngl2uzjXS/OBF/aMxAN06JrJ3qJlF"
	},
	"region": "us-east-1",
	"logger": {
		"file": {
			"enable": false,
			"fileName": "",
			"level": "error"
		},
		"syslog": {
			"enable": false,
			"address": "",
			"level": "debug"
		},
		"console": {
			"enable": true,
			"level": "fatal"
		}
	}
}
```
New command lines in lieu of supporting XL.
Minio initialize filesystem backend.
~~~
$ minio init fs <path>
~~~
Minio initialize XL backend.
~~~
$ minio init xl <url1>...<url16>
~~~
For 'fs' backend it starts the server.
~~~
$ minio server
~~~
For 'xl' backend it waits for servers to join.
~~~
$ minio server
... [PROGRESS BAR] of servers connecting
~~~
Now on other servers execute 'join' and they connect.
~~~
....
minio join <url1> -- from <url2> && minio server
minio join <url1> -- from <url3> && minio server
...
...
minio join <url1> -- from <url16> && minio server
~~~
											
										 
											2016-02-13 07:27:10 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2016-04-30 03:17:48 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-14 03:25:49 +08:00
										 |  |  | func (s *posix) DeleteFileBulk(volume string, paths []string) (errs []error, err error) { | 
					
						
							|  |  |  | 	errs = make([]error, len(paths)) | 
					
						
							|  |  |  | 	for idx, path := range paths { | 
					
						
							|  |  |  | 		errs[idx] = s.DeleteFile(volume, path) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-29 06:13:15 +08:00
										 |  |  | // RenameFile - rename source path to destination path atomically.
 | 
					
						
							| 
									
										
										
										
											2016-06-21 07:57:14 +08:00
										 |  |  | func (s *posix) RenameFile(srcVolume, srcPath, dstVolume, dstPath string) (err error) { | 
					
						
							| 
									
										
										
										
											2016-06-09 13:02:10 +08:00
										 |  |  | 	defer func() { | 
					
						
							| 
									
										
										
										
											2018-07-28 06:32:19 +08:00
										 |  |  | 		if err == errFaultyDisk { | 
					
						
							| 
									
										
										
										
											2016-06-21 07:57:14 +08:00
										 |  |  | 			atomic.AddInt32(&s.ioErrCount, 1) | 
					
						
							| 
									
										
										
										
											2016-06-09 13:02:10 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-01 00:22:53 +08:00
										 |  |  | 	if atomic.LoadInt32(&s.ioErrCount) > maxAllowedIOError { | 
					
						
							| 
									
										
										
										
											2016-06-09 13:02:10 +08:00
										 |  |  | 		return errFaultyDisk | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-01 00:34:44 +08:00
										 |  |  | 	if err = s.checkDiskFound(); err != nil { | 
					
						
							| 
									
										
										
										
											2016-06-03 13:49:27 +08:00
										 |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-18 12:22:27 +08:00
										 |  |  | 	srcVolumeDir, err := s.getVolDir(srcVolume) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	dstVolumeDir, err := s.getVolDir(dstVolume) | 
					
						
							| 
									
										
										
										
											2016-04-30 03:17:48 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-05-18 12:22:27 +08:00
										 |  |  | 	// Stat a volume entry.
 | 
					
						
							| 
									
										
										
										
											2018-02-21 04:20:18 +08:00
										 |  |  | 	_, err = os.Stat(srcVolumeDir) | 
					
						
							| 
									
										
										
										
											2016-04-30 03:17:48 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2016-05-18 12:22:27 +08:00
										 |  |  | 		if os.IsNotExist(err) { | 
					
						
							|  |  |  | 			return errVolumeNotFound | 
					
						
							| 
									
										
										
										
											2018-07-28 06:32:19 +08:00
										 |  |  | 		} else if isSysErrIO(err) { | 
					
						
							|  |  |  | 			return errFaultyDisk | 
					
						
							| 
									
										
										
										
											2016-05-18 12:22:27 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-04-30 03:17:48 +08:00
										 |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-02-21 04:20:18 +08:00
										 |  |  | 	_, err = os.Stat(dstVolumeDir) | 
					
						
							| 
									
										
										
										
											2016-05-18 12:22:27 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		if os.IsNotExist(err) { | 
					
						
							|  |  |  | 			return errVolumeNotFound | 
					
						
							| 
									
										
										
										
											2018-07-28 06:32:19 +08:00
										 |  |  | 		} else if isSysErrIO(err) { | 
					
						
							|  |  |  | 			return errFaultyDisk | 
					
						
							| 
									
										
										
										
											2016-05-18 12:22:27 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-17 06:52:14 +08:00
										 |  |  | 	srcIsDir := hasSuffix(srcPath, slashSeparator) | 
					
						
							|  |  |  | 	dstIsDir := hasSuffix(dstPath, slashSeparator) | 
					
						
							| 
									
										
										
										
											2016-05-17 05:31:28 +08:00
										 |  |  | 	// Either src and dst have to be directories or files, else return error.
 | 
					
						
							| 
									
										
										
										
											2016-05-14 02:52:36 +08:00
										 |  |  | 	if !(srcIsDir && dstIsDir || !srcIsDir && !dstIsDir) { | 
					
						
							|  |  |  | 		return errFileAccessDenied | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-06-18 02:57:51 +08:00
										 |  |  | 	srcFilePath := slashpath.Join(srcVolumeDir, srcPath) | 
					
						
							| 
									
										
										
										
											2018-02-21 04:20:18 +08:00
										 |  |  | 	if err = checkPathLength(srcFilePath); err != nil { | 
					
						
							| 
									
										
										
										
											2016-06-18 02:57:51 +08:00
										 |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	dstFilePath := slashpath.Join(dstVolumeDir, dstPath) | 
					
						
							| 
									
										
										
										
											2018-02-21 04:20:18 +08:00
										 |  |  | 	if err = checkPathLength(dstFilePath); err != nil { | 
					
						
							| 
									
										
										
										
											2016-06-18 02:57:51 +08:00
										 |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-05-14 02:52:36 +08:00
										 |  |  | 	if srcIsDir { | 
					
						
							| 
									
										
										
										
											2018-02-21 04:20:18 +08:00
										 |  |  | 		// If source is a directory, we expect the destination to be non-existent but we
 | 
					
						
							|  |  |  | 		// we still need to allow overwriting an empty directory since it represents
 | 
					
						
							|  |  |  | 		// an object empty directory.
 | 
					
						
							|  |  |  | 		_, err = os.Stat(dstFilePath) | 
					
						
							| 
									
										
										
										
											2018-07-28 06:32:19 +08:00
										 |  |  | 		if isSysErrIO(err) { | 
					
						
							|  |  |  | 			return errFaultyDisk | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-02-21 04:20:18 +08:00
										 |  |  | 		if err == nil && !isDirEmpty(dstFilePath) { | 
					
						
							| 
									
										
										
										
											2016-05-14 02:52:36 +08:00
										 |  |  | 			return errFileAccessDenied | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-03-27 05:57:44 +08:00
										 |  |  | 		if err != nil && !os.IsNotExist(err) { | 
					
						
							| 
									
										
										
										
											2016-05-14 02:52:36 +08:00
										 |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-03-27 05:57:44 +08:00
										 |  |  | 		// Empty destination remove it before rename.
 | 
					
						
							|  |  |  | 		if isDirEmpty(dstFilePath) { | 
					
						
							|  |  |  | 			if err = os.Remove(dstFilePath); err != nil { | 
					
						
							|  |  |  | 				if isSysErrNotEmpty(err) { | 
					
						
							|  |  |  | 					return errFileAccessDenied | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				return err | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-05-14 02:52:36 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-01-14 01:13:02 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if err = renameAll(srcFilePath, dstFilePath); err != nil { | 
					
						
							| 
									
										
										
										
											2018-07-28 06:32:19 +08:00
										 |  |  | 		if isSysErrIO(err) { | 
					
						
							|  |  |  | 			return errFaultyDisk | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-05-04 07:10:24 +08:00
										 |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-11-22 08:34:57 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Remove parent dir of the source file if empty
 | 
					
						
							| 
									
										
										
										
											2016-12-01 12:56:16 +08:00
										 |  |  | 	if parentDir := slashpath.Dir(srcFilePath); isDirEmpty(parentDir) { | 
					
						
							| 
									
										
										
										
											2016-11-22 08:34:57 +08:00
										 |  |  | 		deleteFile(srcVolumeDir, parentDir) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-04 07:10:24 +08:00
										 |  |  | 	return nil | 
					
						
							| 
									
										
										
										
											2016-04-30 03:17:48 +08:00
										 |  |  | } |