storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/cmd/xl-storage.go (about)

     1  /*
     2   * MinIO Cloud Storage, (C) 2016-2020 MinIO, Inc.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package cmd
    18  
    19  import (
    20  	"bufio"
    21  	"bytes"
    22  	"context"
    23  	"crypto/rand"
    24  	"encoding/hex"
    25  	"errors"
    26  	"fmt"
    27  	"io"
    28  	"io/ioutil"
    29  	"net/url"
    30  	"os"
    31  	pathutil "path"
    32  	"path/filepath"
    33  	"runtime"
    34  	"strings"
    35  	"sync"
    36  	"time"
    37  
    38  	"github.com/dustin/go-humanize"
    39  	"github.com/google/uuid"
    40  	jsoniter "github.com/json-iterator/go"
    41  	"github.com/klauspost/readahead"
    42  
    43  	"storj.io/minio/cmd/config"
    44  	"storj.io/minio/cmd/config/storageclass"
    45  	"storj.io/minio/cmd/logger"
    46  	"storj.io/minio/pkg/bucket/lifecycle"
    47  	"storj.io/minio/pkg/color"
    48  	"storj.io/minio/pkg/console"
    49  	"storj.io/minio/pkg/disk"
    50  	"storj.io/minio/pkg/env"
    51  	xioutil "storj.io/minio/pkg/ioutil"
    52  )
    53  
    54  const (
    55  	nullVersionID  = "null"
    56  	blockSizeLarge = 2 * humanize.MiByte   // Default r/w block size for larger objects.
    57  	blockSizeSmall = 128 * humanize.KiByte // Default r/w block size for smaller objects.
    58  
    59  	// On regular files bigger than this;
    60  	readAheadSize = 16 << 20
    61  	// Read this many buffers ahead.
    62  	readAheadBuffers = 4
    63  	// Size of each buffer.
    64  	readAheadBufSize = 1 << 20
    65  
    66  	// Small file threshold below which data accompanies metadata from storage layer.
    67  	smallFileThreshold = 128 * humanize.KiByte // Optimized for NVMe/SSDs
    68  	// For hardrives it is possible to set this to a lower value to avoid any
    69  	// spike in latency. But currently we are simply keeping it optimal for SSDs.
    70  
    71  	// XL metadata file carries per object metadata.
    72  	xlStorageFormatFile = "xl.meta"
    73  )
    74  
    75  var alignedBuf []byte
    76  
    77  func init() {
    78  	alignedBuf = disk.AlignedBlock(4096)
    79  	_, _ = rand.Read(alignedBuf)
    80  }
    81  
    82  // isValidVolname verifies a volname name in accordance with object
    83  // layer requirements.
    84  func isValidVolname(volname string) bool {
    85  	if len(volname) < 3 {
    86  		return false
    87  	}
    88  
    89  	if runtime.GOOS == "windows" {
    90  		// Volname shouldn't have reserved characters in Windows.
    91  		return !strings.ContainsAny(volname, `\:*?\"<>|`)
    92  	}
    93  
    94  	return true
    95  }
    96  
    97  // xlStorage - implements StorageAPI interface.
    98  type xlStorage struct {
    99  	diskPath string
   100  	endpoint Endpoint
   101  
   102  	globalSync bool
   103  
   104  	poolLarge sync.Pool
   105  	poolSmall sync.Pool
   106  
   107  	rootDisk bool
   108  
   109  	diskID string
   110  
   111  	// Indexes, will be -1 until assigned a set.
   112  	poolIndex, setIndex, diskIndex int
   113  
   114  	formatFileInfo  os.FileInfo
   115  	formatLegacy    bool
   116  	formatLastCheck time.Time
   117  
   118  	diskInfoCache timedValue
   119  
   120  	ctx context.Context
   121  	sync.RWMutex
   122  }
   123  
   124  // checkPathLength - returns error if given path name length more than 255
   125  func checkPathLength(pathName string) error {
   126  	// Apple OS X path length is limited to 1016
   127  	if runtime.GOOS == "darwin" && len(pathName) > 1016 {
   128  		return errFileNameTooLong
   129  	}
   130  
   131  	// Disallow more than 1024 characters on windows, there
   132  	// are no known name_max limits on Windows.
   133  	if runtime.GOOS == "windows" && len(pathName) > 1024 {
   134  		return errFileNameTooLong
   135  	}
   136  
   137  	// On Unix we reject paths if they are just '.', '..' or '/'
   138  	if pathName == "." || pathName == ".." || pathName == slashSeparator {
   139  		return errFileAccessDenied
   140  	}
   141  
   142  	// Check each path segment length is > 255 on all Unix
   143  	// platforms, look for this value as NAME_MAX in
   144  	// /usr/include/linux/limits.h
   145  	var count int64
   146  	for _, p := range pathName {
   147  		switch p {
   148  		case '/':
   149  			count = 0 // Reset
   150  		case '\\':
   151  			if runtime.GOOS == globalWindowsOSName {
   152  				count = 0
   153  			}
   154  		default:
   155  			count++
   156  			if count > 255 {
   157  				return errFileNameTooLong
   158  			}
   159  		}
   160  	} // Success.
   161  	return nil
   162  }
   163  
   164  func getValidPath(path string) (string, error) {
   165  	if path == "" {
   166  		return path, errInvalidArgument
   167  	}
   168  
   169  	var err error
   170  	// Disallow relative paths, figure out absolute paths.
   171  	path, err = filepath.Abs(path)
   172  	if err != nil {
   173  		return path, err
   174  	}
   175  
   176  	fi, err := Lstat(path)
   177  	if err != nil && !osIsNotExist(err) {
   178  		return path, err
   179  	}
   180  	if osIsNotExist(err) {
   181  		// Disk not found create it.
   182  		if err = reliableMkdirAll(path, 0777); err != nil {
   183  			return path, err
   184  		}
   185  	}
   186  	if fi != nil && !fi.IsDir() {
   187  		return path, errDiskNotDir
   188  	}
   189  
   190  	return path, nil
   191  }
   192  
   193  // isDirEmpty - returns whether given directory is empty or not.
   194  func isDirEmpty(dirname string) bool {
   195  	entries, err := readDirN(dirname, 1)
   196  	if err != nil {
   197  		if err != errFileNotFound {
   198  			logger.LogIf(GlobalContext, err)
   199  		}
   200  		return false
   201  	}
   202  	return len(entries) == 0
   203  }
   204  
   205  // Initialize a new storage disk.
   206  func newLocalXLStorage(path string) (*xlStorage, error) {
   207  	u := url.URL{Path: path}
   208  	return newXLStorage(Endpoint{
   209  		URL:     &u,
   210  		IsLocal: true,
   211  	})
   212  }
   213  
   214  // Initialize a new storage disk.
   215  func newXLStorage(ep Endpoint) (*xlStorage, error) {
   216  	path := ep.Path
   217  	var err error
   218  	if path, err = getValidPath(path); err != nil {
   219  		return nil, err
   220  	}
   221  
   222  	var rootDisk bool
   223  	if env.Get("MINIO_CI_CD", "") != "" {
   224  		rootDisk = true
   225  	} else {
   226  		if IsDocker() || IsKubernetes() {
   227  			// Start with overlay "/" to check if
   228  			// possible the path has device id as
   229  			// "overlay" that would mean the path
   230  			// is emphemeral and we should treat it
   231  			// as root disk from the baremetal
   232  			// terminology.
   233  			rootDisk, err = disk.IsRootDisk(path, SlashSeparator)
   234  			if err != nil {
   235  				return nil, err
   236  			}
   237  			if !rootDisk {
   238  				// No root disk was found, its possible that
   239  				// path is referenced at "/etc/hosts" which has
   240  				// different device ID that points to the original
   241  				// "/" on the host system, fall back to that instead
   242  				// to verify of the device id is same.
   243  				rootDisk, err = disk.IsRootDisk(path, "/etc/hosts")
   244  				if err != nil {
   245  					return nil, err
   246  				}
   247  			}
   248  
   249  		} else {
   250  			// On baremetal setups its always "/" is the root disk.
   251  			rootDisk, err = disk.IsRootDisk(path, SlashSeparator)
   252  			if err != nil {
   253  				return nil, err
   254  			}
   255  		}
   256  	}
   257  
   258  	p := &xlStorage{
   259  		diskPath: path,
   260  		endpoint: ep,
   261  		poolLarge: sync.Pool{
   262  			New: func() interface{} {
   263  				b := disk.AlignedBlock(blockSizeLarge)
   264  				return &b
   265  			},
   266  		},
   267  		poolSmall: sync.Pool{
   268  			New: func() interface{} {
   269  				b := disk.AlignedBlock(blockSizeSmall)
   270  				return &b
   271  			},
   272  		},
   273  		globalSync: env.Get(config.EnvFSOSync, config.EnableOff) == config.EnableOn,
   274  		ctx:        GlobalContext,
   275  		rootDisk:   rootDisk,
   276  		poolIndex:  -1,
   277  		setIndex:   -1,
   278  		diskIndex:  -1,
   279  	}
   280  
   281  	// Create all necessary bucket folders if possible.
   282  	if err = p.MakeVolBulk(context.TODO(), minioMetaBucket, minioMetaTmpBucket, minioMetaMultipartBucket, dataUsageBucket); err != nil {
   283  		return nil, err
   284  	}
   285  
   286  	// Check if backend is writable and supports O_DIRECT
   287  	var rnd [8]byte
   288  	_, _ = rand.Read(rnd[:])
   289  	tmpFile := ".writable-check-" + hex.EncodeToString(rnd[:]) + ".tmp"
   290  	filePath := pathJoin(p.diskPath, minioMetaTmpBucket, tmpFile)
   291  	w, err := disk.OpenFileDirectIO(filePath, os.O_CREATE|os.O_WRONLY|os.O_EXCL, 0666)
   292  	if err != nil {
   293  		return p, err
   294  	}
   295  	if _, err = w.Write(alignedBuf[:]); err != nil {
   296  		w.Close()
   297  		return p, err
   298  	}
   299  	w.Close()
   300  	defer Remove(filePath)
   301  
   302  	// Success.
   303  	return p, nil
   304  }
   305  
   306  // getDiskInfo returns given disk information.
   307  func getDiskInfo(diskPath string) (di disk.Info, err error) {
   308  	if err = checkPathLength(diskPath); err == nil {
   309  		di, err = disk.GetInfo(diskPath)
   310  	}
   311  
   312  	switch {
   313  	case osIsNotExist(err):
   314  		err = errDiskNotFound
   315  	case isSysErrTooLong(err):
   316  		err = errFileNameTooLong
   317  	case isSysErrIO(err):
   318  		err = errFaultyDisk
   319  	}
   320  
   321  	return di, err
   322  }
   323  
   324  // Implements stringer compatible interface.
   325  func (s *xlStorage) String() string {
   326  	return s.diskPath
   327  }
   328  
   329  func (s *xlStorage) Hostname() string {
   330  	return s.endpoint.Host
   331  }
   332  
   333  func (s *xlStorage) Endpoint() Endpoint {
   334  	return s.endpoint
   335  }
   336  
   337  func (*xlStorage) Close() error {
   338  	return nil
   339  }
   340  
   341  func (s *xlStorage) IsOnline() bool {
   342  	return true
   343  }
   344  
   345  func (s *xlStorage) IsLocal() bool {
   346  	return true
   347  }
   348  
   349  // Retrieve location indexes.
   350  func (s *xlStorage) GetDiskLoc() (poolIdx, setIdx, diskIdx int) {
   351  	s.RLock()
   352  	defer s.RUnlock()
   353  	// If unset, see if we can locate it.
   354  	if s.poolIndex < 0 || s.setIndex < 0 || s.diskIndex < 0 {
   355  		return getXLDiskLoc(s.diskID)
   356  	}
   357  	return s.poolIndex, s.setIndex, s.diskIndex
   358  }
   359  
   360  // Set location indexes.
   361  func (s *xlStorage) SetDiskLoc(poolIdx, setIdx, diskIdx int) {
   362  	s.poolIndex = poolIdx
   363  	s.setIndex = setIdx
   364  	s.diskIndex = diskIdx
   365  }
   366  
   367  func (s *xlStorage) Healing() *healingTracker {
   368  	healingFile := pathJoin(s.diskPath, minioMetaBucket,
   369  		bucketMetaPrefix, healingTrackerFilename)
   370  	b, err := ioutil.ReadFile(healingFile)
   371  	if err != nil {
   372  		return nil
   373  	}
   374  	var h healingTracker
   375  	_, err = h.UnmarshalMsg(b)
   376  	logger.LogIf(GlobalContext, err)
   377  	return &h
   378  }
   379  
   380  func (s *xlStorage) NSScanner(ctx context.Context, cache dataUsageCache) (dataUsageCache, error) {
   381  	var lc *lifecycle.Lifecycle
   382  	var err error
   383  
   384  	// Check if the current bucket has a configured lifecycle policy
   385  	if globalLifecycleSys != nil {
   386  		lc, err = globalLifecycleSys.Get(cache.Info.Name)
   387  		if err == nil && lc.HasActiveRules("", true) {
   388  			cache.Info.lifeCycle = lc
   389  			if intDataUpdateTracker.debug {
   390  				console.Debugln(color.Green("scannerDisk:") + " lifecycle: Active rules found")
   391  			}
   392  		}
   393  	}
   394  
   395  	// return initialized object layer
   396  	objAPI := newObjectLayerFn()
   397  
   398  	globalHealConfigMu.Lock()
   399  	healOpts := globalHealConfig
   400  	globalHealConfigMu.Unlock()
   401  
   402  	dataUsageInfo, err := scanDataFolder(ctx, s.diskPath, cache, func(item scannerItem) (sizeSummary, error) {
   403  		// Look for `xl.meta/xl.json' at the leaf.
   404  		if !strings.HasSuffix(item.Path, SlashSeparator+xlStorageFormatFile) &&
   405  			!strings.HasSuffix(item.Path, SlashSeparator+xlStorageFormatFileV1) {
   406  			// if no xl.meta/xl.json found, skip the file.
   407  			return sizeSummary{}, errSkipFile
   408  		}
   409  
   410  		buf, err := xioutil.ReadFile(item.Path)
   411  		if err != nil {
   412  			if intDataUpdateTracker.debug {
   413  				console.Debugf(color.Green("scannerBucket:")+" object path missing: %v: %w\n", item.Path, err)
   414  			}
   415  			return sizeSummary{}, errSkipFile
   416  		}
   417  
   418  		// Remove filename which is the meta file.
   419  		item.transformMetaDir()
   420  
   421  		fivs, err := getFileInfoVersions(buf, item.bucket, item.objectPath())
   422  		if err != nil {
   423  			if intDataUpdateTracker.debug {
   424  				console.Debugf(color.Green("scannerBucket:")+" reading xl.meta failed: %v: %w\n", item.Path, err)
   425  			}
   426  			return sizeSummary{}, errSkipFile
   427  		}
   428  
   429  		var totalSize int64
   430  
   431  		sizeS := sizeSummary{}
   432  		for _, version := range fivs.Versions {
   433  			oi := version.ToObjectInfo(item.bucket, item.objectPath())
   434  			if objAPI != nil {
   435  				totalSize += item.applyActions(ctx, objAPI, actionMeta{
   436  					oi:         oi,
   437  					bitRotScan: healOpts.Bitrot,
   438  				})
   439  				item.healReplication(ctx, objAPI, oi.Clone(), &sizeS)
   440  			}
   441  		}
   442  		sizeS.totalSize = totalSize
   443  		return sizeS, nil
   444  	})
   445  
   446  	if err != nil {
   447  		return dataUsageInfo, err
   448  	}
   449  
   450  	dataUsageInfo.Info.LastUpdate = time.Now()
   451  	return dataUsageInfo, nil
   452  }
   453  
   454  // DiskInfo provides current information about disk space usage,
   455  // total free inodes and underlying filesystem.
   456  func (s *xlStorage) DiskInfo(context.Context) (info DiskInfo, err error) {
   457  	s.diskInfoCache.Once.Do(func() {
   458  		s.diskInfoCache.TTL = time.Second
   459  		s.diskInfoCache.Update = func() (interface{}, error) {
   460  			dcinfo := DiskInfo{
   461  				RootDisk:  s.rootDisk,
   462  				MountPath: s.diskPath,
   463  				Endpoint:  s.endpoint.String(),
   464  			}
   465  			di, err := getDiskInfo(s.diskPath)
   466  			if err != nil {
   467  				return dcinfo, err
   468  			}
   469  			dcinfo.Total = di.Total
   470  			dcinfo.Free = di.Free
   471  			dcinfo.Used = di.Used
   472  			dcinfo.UsedInodes = di.Files - di.Ffree
   473  			dcinfo.FSType = di.FSType
   474  
   475  			diskID, err := s.GetDiskID()
   476  			if errors.Is(err, errUnformattedDisk) {
   477  				// if we found an unformatted disk then
   478  				// healing is automatically true.
   479  				dcinfo.Healing = true
   480  			} else {
   481  				// Check if the disk is being healed if GetDiskID
   482  				// returned any error other than fresh disk
   483  				dcinfo.Healing = s.Healing() != nil
   484  			}
   485  
   486  			dcinfo.ID = diskID
   487  			return dcinfo, err
   488  		}
   489  	})
   490  
   491  	v, err := s.diskInfoCache.Get()
   492  	info = v.(DiskInfo)
   493  	return info, err
   494  }
   495  
   496  // getVolDir - will convert incoming volume names to
   497  // corresponding valid volume names on the backend in a platform
   498  // compatible way for all operating systems. If volume is not found
   499  // an error is generated.
   500  func (s *xlStorage) getVolDir(volume string) (string, error) {
   501  	if volume == "" || volume == "." || volume == ".." {
   502  		return "", errVolumeNotFound
   503  	}
   504  	volumeDir := pathJoin(s.diskPath, volume)
   505  	return volumeDir, nil
   506  }
   507  
   508  // GetDiskID - returns the cached disk uuid
   509  func (s *xlStorage) GetDiskID() (string, error) {
   510  	s.RLock()
   511  	diskID := s.diskID
   512  	fileInfo := s.formatFileInfo
   513  	lastCheck := s.formatLastCheck
   514  	s.RUnlock()
   515  
   516  	// check if we have a valid disk ID that is less than 1 second old.
   517  	if fileInfo != nil && diskID != "" && time.Since(lastCheck) <= time.Second {
   518  		return diskID, nil
   519  	}
   520  
   521  	s.Lock()
   522  	// If somebody else updated the disk ID and changed the time, return what they got.
   523  	if !lastCheck.IsZero() && !s.formatLastCheck.Equal(lastCheck) && diskID != "" {
   524  		s.Unlock()
   525  		// Somebody else got the lock first.
   526  		return diskID, nil
   527  	}
   528  	s.Unlock()
   529  
   530  	formatFile := pathJoin(s.diskPath, minioMetaBucket, formatConfigFile)
   531  	fi, err := Lstat(formatFile)
   532  	if err != nil {
   533  		// If the disk is still not initialized.
   534  		if osIsNotExist(err) {
   535  			if err = Access(s.diskPath); err == nil {
   536  				// Disk is present but missing `format.json`
   537  				return "", errUnformattedDisk
   538  			}
   539  			if osIsNotExist(err) {
   540  				return "", errDiskNotFound
   541  			} else if osIsPermission(err) {
   542  				return "", errDiskAccessDenied
   543  			}
   544  			logger.LogIf(GlobalContext, err) // log unexpected errors
   545  			return "", errCorruptedFormat
   546  		} else if osIsPermission(err) {
   547  			return "", errDiskAccessDenied
   548  		}
   549  		logger.LogIf(GlobalContext, err) // log unexpected errors
   550  		return "", errCorruptedFormat
   551  	}
   552  
   553  	if xioutil.SameFile(fi, fileInfo) && diskID != "" {
   554  		s.Lock()
   555  		// If the file has not changed, just return the cached diskID information.
   556  		s.formatLastCheck = time.Now()
   557  		s.Unlock()
   558  		return diskID, nil
   559  	}
   560  
   561  	b, err := xioutil.ReadFile(formatFile)
   562  	if err != nil {
   563  		// If the disk is still not initialized.
   564  		if osIsNotExist(err) {
   565  			if err = Access(s.diskPath); err == nil {
   566  				// Disk is present but missing `format.json`
   567  				return "", errUnformattedDisk
   568  			}
   569  			if osIsNotExist(err) {
   570  				return "", errDiskNotFound
   571  			} else if osIsPermission(err) {
   572  				return "", errDiskAccessDenied
   573  			}
   574  			logger.LogIf(GlobalContext, err) // log unexpected errors
   575  			return "", errCorruptedFormat
   576  		} else if osIsPermission(err) {
   577  			return "", errDiskAccessDenied
   578  		}
   579  		logger.LogIf(GlobalContext, err) // log unexpected errors
   580  		return "", errCorruptedFormat
   581  	}
   582  
   583  	format := &formatErasureV3{}
   584  	var json = jsoniter.ConfigCompatibleWithStandardLibrary
   585  	if err = json.Unmarshal(b, &format); err != nil {
   586  		logger.LogIf(GlobalContext, err) // log unexpected errors
   587  		return "", errCorruptedFormat
   588  	}
   589  
   590  	s.Lock()
   591  	defer s.Unlock()
   592  	s.diskID = format.Erasure.This
   593  	s.formatLegacy = format.Erasure.DistributionAlgo == formatErasureVersionV2DistributionAlgoV1
   594  	s.formatFileInfo = fi
   595  	s.formatLastCheck = time.Now()
   596  	return s.diskID, nil
   597  }
   598  
   599  // Make a volume entry.
   600  func (s *xlStorage) SetDiskID(id string) {
   601  	// NO-OP for xlStorage as it is handled either by xlStorageDiskIDCheck{} for local disks or
   602  	// storage rest server for remote disks.
   603  }
   604  
   605  func (s *xlStorage) MakeVolBulk(ctx context.Context, volumes ...string) error {
   606  	for _, volume := range volumes {
   607  		if err := s.MakeVol(ctx, volume); err != nil {
   608  			if errors.Is(err, errDiskAccessDenied) {
   609  				return errDiskAccessDenied
   610  			}
   611  		}
   612  	}
   613  	return nil
   614  }
   615  
   616  // Make a volume entry.
   617  func (s *xlStorage) MakeVol(ctx context.Context, volume string) error {
   618  	if !isValidVolname(volume) {
   619  		return errInvalidArgument
   620  	}
   621  
   622  	volumeDir, err := s.getVolDir(volume)
   623  	if err != nil {
   624  		return err
   625  	}
   626  
   627  	if err = Access(volumeDir); err != nil {
   628  		// Volume does not exist we proceed to create.
   629  		if osIsNotExist(err) {
   630  			// Make a volume entry, with mode 0777 mkdir honors system umask.
   631  			err = reliableMkdirAll(volumeDir, 0777)
   632  		}
   633  		if osIsPermission(err) {
   634  			return errDiskAccessDenied
   635  		} else if isSysErrIO(err) {
   636  			return errFaultyDisk
   637  		}
   638  		return err
   639  	}
   640  
   641  	// Stat succeeds we return errVolumeExists.
   642  	return errVolumeExists
   643  }
   644  
   645  // ListVols - list volumes.
   646  func (s *xlStorage) ListVols(context.Context) (volsInfo []VolInfo, err error) {
   647  	return listVols(s.diskPath)
   648  }
   649  
   650  // List all the volumes from diskPath.
   651  func listVols(dirPath string) ([]VolInfo, error) {
   652  	if err := checkPathLength(dirPath); err != nil {
   653  		return nil, err
   654  	}
   655  	entries, err := readDir(dirPath)
   656  	if err != nil {
   657  		return nil, errDiskNotFound
   658  	}
   659  	volsInfo := make([]VolInfo, 0, len(entries))
   660  	for _, entry := range entries {
   661  		if !HasSuffix(entry, SlashSeparator) || !isValidVolname(pathutil.Clean(entry)) {
   662  			// Skip if entry is neither a directory not a valid volume name.
   663  			continue
   664  		}
   665  		volsInfo = append(volsInfo, VolInfo{
   666  			Name: pathutil.Clean(entry),
   667  		})
   668  	}
   669  	return volsInfo, nil
   670  }
   671  
   672  // StatVol - get volume info.
   673  func (s *xlStorage) StatVol(ctx context.Context, volume string) (vol VolInfo, err error) {
   674  	// Verify if volume is valid and it exists.
   675  	volumeDir, err := s.getVolDir(volume)
   676  	if err != nil {
   677  		return VolInfo{}, err
   678  	}
   679  
   680  	// Stat a volume entry.
   681  	var st os.FileInfo
   682  	st, err = Lstat(volumeDir)
   683  	if err != nil {
   684  		switch {
   685  		case osIsNotExist(err):
   686  			return VolInfo{}, errVolumeNotFound
   687  		case osIsPermission(err):
   688  			return VolInfo{}, errDiskAccessDenied
   689  		case isSysErrIO(err):
   690  			return VolInfo{}, errFaultyDisk
   691  		default:
   692  			return VolInfo{}, err
   693  		}
   694  	}
   695  	// As os.Lstat() doesn't carry other than ModTime(), use ModTime()
   696  	// as CreatedTime.
   697  	createdTime := st.ModTime()
   698  	return VolInfo{
   699  		Name:    volume,
   700  		Created: createdTime,
   701  	}, nil
   702  }
   703  
   704  // DeleteVol - delete a volume.
   705  func (s *xlStorage) DeleteVol(ctx context.Context, volume string, forceDelete bool) (err error) {
   706  	// Verify if volume is valid and it exists.
   707  	volumeDir, err := s.getVolDir(volume)
   708  	if err != nil {
   709  		return err
   710  	}
   711  
   712  	if forceDelete {
   713  		err = RemoveAll(volumeDir)
   714  	} else {
   715  		err = Remove(volumeDir)
   716  	}
   717  
   718  	if err != nil {
   719  		switch {
   720  		case osIsNotExist(err):
   721  			return errVolumeNotFound
   722  		case isSysErrNotEmpty(err):
   723  			return errVolumeNotEmpty
   724  		case osIsPermission(err):
   725  			return errDiskAccessDenied
   726  		case isSysErrIO(err):
   727  			return errFaultyDisk
   728  		default:
   729  			return err
   730  		}
   731  	}
   732  	return nil
   733  }
   734  
   735  func (s *xlStorage) isLeaf(volume string, leafPath string) bool {
   736  	volumeDir, err := s.getVolDir(volume)
   737  	if err != nil {
   738  		return false
   739  	}
   740  
   741  	if err = Access(pathJoin(volumeDir, leafPath, xlStorageFormatFile)); err == nil {
   742  		return true
   743  	}
   744  	if osIsNotExist(err) {
   745  		// We need a fallback code where directory might contain
   746  		// legacy `xl.json`, in such situation we just rename
   747  		// and proceed if rename is successful we know that it
   748  		// is the leaf since `xl.json` was present.
   749  		return s.renameLegacyMetadata(volumeDir, leafPath) == nil
   750  	}
   751  	return false
   752  }
   753  
   754  // ListDir - return all the entries at the given directory path.
   755  // If an entry is a directory it will be returned with a trailing SlashSeparator.
   756  func (s *xlStorage) ListDir(ctx context.Context, volume, dirPath string, count int) (entries []string, err error) {
   757  	// Verify if volume is valid and it exists.
   758  	volumeDir, err := s.getVolDir(volume)
   759  	if err != nil {
   760  		return nil, err
   761  	}
   762  
   763  	dirPathAbs := pathJoin(volumeDir, dirPath)
   764  	if count > 0 {
   765  		entries, err = readDirN(dirPathAbs, count)
   766  	} else {
   767  		entries, err = readDir(dirPathAbs)
   768  	}
   769  	if err != nil {
   770  		if err == errFileNotFound {
   771  			if ierr := Access(volumeDir); ierr != nil {
   772  				if osIsNotExist(ierr) {
   773  					return nil, errVolumeNotFound
   774  				} else if isSysErrIO(ierr) {
   775  					return nil, errFaultyDisk
   776  				}
   777  			}
   778  		}
   779  		return nil, err
   780  	}
   781  
   782  	return entries, nil
   783  }
   784  
   785  // DeleteVersions deletes slice of versions, it can be same object
   786  // or multiple objects.
   787  func (s *xlStorage) DeleteVersions(ctx context.Context, volume string, versions []FileInfo) []error {
   788  	errs := make([]error, len(versions))
   789  
   790  	for i, version := range versions {
   791  		if err := s.DeleteVersion(ctx, volume, version.Name, version, false); err != nil {
   792  			errs[i] = err
   793  		}
   794  	}
   795  
   796  	return errs
   797  }
   798  
   799  // DeleteVersion - deletes FileInfo metadata for path at `xl.meta`. forceDelMarker
   800  // will force creating a new `xl.meta` to create a new delete marker
   801  func (s *xlStorage) DeleteVersion(ctx context.Context, volume, path string, fi FileInfo, forceDelMarker bool) error {
   802  	if HasSuffix(path, SlashSeparator) {
   803  		return s.Delete(ctx, volume, path, false)
   804  	}
   805  
   806  	buf, err := s.ReadAll(ctx, volume, pathJoin(path, xlStorageFormatFile))
   807  	if err != nil {
   808  		if err != errFileNotFound {
   809  			return err
   810  		}
   811  		if fi.Deleted && forceDelMarker {
   812  			// Create a new xl.meta with a delete marker in it
   813  			return s.WriteMetadata(ctx, volume, path, fi)
   814  		}
   815  		if fi.VersionID != "" {
   816  			return errFileVersionNotFound
   817  		}
   818  		return errFileNotFound
   819  	}
   820  
   821  	if len(buf) == 0 {
   822  		if fi.VersionID != "" {
   823  			return errFileVersionNotFound
   824  		}
   825  		return errFileNotFound
   826  	}
   827  
   828  	volumeDir, err := s.getVolDir(volume)
   829  	if err != nil {
   830  		return err
   831  	}
   832  
   833  	if !isXL2V1Format(buf) {
   834  		// Delete the meta file, if there are no more versions the
   835  		// top level parent is automatically removed.
   836  		return s.deleteFile(volumeDir, pathJoin(volumeDir, path), true)
   837  	}
   838  
   839  	var xlMeta xlMetaV2
   840  	if err = xlMeta.Load(buf); err != nil {
   841  		return err
   842  	}
   843  
   844  	dataDir, lastVersion, err := xlMeta.DeleteVersion(fi)
   845  	if err != nil {
   846  		return err
   847  	}
   848  
   849  	// transitioned objects maintains metadata on the source cluster. When transition
   850  	// status is set, update the metadata to disk.
   851  	if !lastVersion || fi.TransitionStatus != "" {
   852  		// when data-dir is specified. Transition leverages existing DeleteObject
   853  		// api call to mark object as deleted. When object is pending transition,
   854  		// just update the metadata and avoid deleting data dir.
   855  		if dataDir != "" && fi.TransitionStatus != lifecycle.TransitionPending {
   856  			versionID := fi.VersionID
   857  			if versionID == "" {
   858  				versionID = nullVersionID
   859  			}
   860  			xlMeta.data.remove(versionID)
   861  			// PR #11758 used DataDir, preserve it
   862  			// for users who might have used master
   863  			// branch
   864  			xlMeta.data.remove(dataDir)
   865  
   866  			filePath := pathJoin(volumeDir, path, dataDir)
   867  			if err = checkPathLength(filePath); err != nil {
   868  				return err
   869  			}
   870  
   871  			tmpuuid := mustGetUUID()
   872  			if err = renameAll(filePath, pathutil.Join(s.diskPath, minioMetaTmpDeletedBucket, tmpuuid)); err != nil {
   873  				if err != errFileNotFound {
   874  					return err
   875  				}
   876  			}
   877  		}
   878  
   879  		buf, err = xlMeta.AppendTo(nil)
   880  		if err != nil {
   881  			return err
   882  		}
   883  
   884  		return s.WriteAll(ctx, volume, pathJoin(path, xlStorageFormatFile), buf)
   885  	}
   886  
   887  	// Move everything to trash.
   888  	filePath := retainSlash(pathJoin(volumeDir, path))
   889  	if err = checkPathLength(filePath); err != nil {
   890  		return err
   891  	}
   892  	err = renameAll(filePath, pathutil.Join(s.diskPath, minioMetaTmpDeletedBucket, mustGetUUID()))
   893  
   894  	// Delete parents if needed.
   895  	filePath = retainSlash(pathutil.Dir(pathJoin(volumeDir, path)))
   896  	if filePath == retainSlash(volumeDir) {
   897  		return err
   898  	}
   899  	s.deleteFile(volumeDir, filePath, false)
   900  	return err
   901  }
   902  
   903  // Updates only metadata for a given version.
   904  func (s *xlStorage) UpdateMetadata(ctx context.Context, volume, path string, fi FileInfo) error {
   905  	if len(fi.Metadata) == 0 {
   906  		return errInvalidArgument
   907  	}
   908  
   909  	buf, err := s.ReadAll(ctx, volume, pathJoin(path, xlStorageFormatFile))
   910  	if err != nil {
   911  		if err == errFileNotFound {
   912  			if fi.VersionID != "" {
   913  				return errFileVersionNotFound
   914  			}
   915  		}
   916  		return err
   917  	}
   918  
   919  	if !isXL2V1Format(buf) {
   920  		return errFileVersionNotFound
   921  	}
   922  
   923  	var xlMeta xlMetaV2
   924  	if err = xlMeta.Load(buf); err != nil {
   925  		logger.LogIf(ctx, err)
   926  		return err
   927  	}
   928  
   929  	if err = xlMeta.UpdateObjectVersion(fi); err != nil {
   930  		return err
   931  	}
   932  
   933  	buf, err = xlMeta.AppendTo(nil)
   934  	if err != nil {
   935  		return err
   936  	}
   937  
   938  	return s.WriteAll(ctx, volume, pathJoin(path, xlStorageFormatFile), buf)
   939  }
   940  
   941  // WriteMetadata - writes FileInfo metadata for path at `xl.meta`
   942  func (s *xlStorage) WriteMetadata(ctx context.Context, volume, path string, fi FileInfo) error {
   943  	buf, err := s.ReadAll(ctx, volume, pathJoin(path, xlStorageFormatFile))
   944  	if err != nil && err != errFileNotFound {
   945  		return err
   946  	}
   947  
   948  	var xlMeta xlMetaV2
   949  	if !isXL2V1Format(buf) {
   950  		xlMeta, err = newXLMetaV2(fi)
   951  		if err != nil {
   952  			logger.LogIf(ctx, err)
   953  			return err
   954  		}
   955  
   956  		buf, err = xlMeta.AppendTo(nil)
   957  		if err != nil {
   958  			logger.LogIf(ctx, err)
   959  			return err
   960  		}
   961  	} else {
   962  		if err = xlMeta.Load(buf); err != nil {
   963  			logger.LogIf(ctx, err)
   964  			return err
   965  		}
   966  
   967  		if err = xlMeta.AddVersion(fi); err != nil {
   968  			logger.LogIf(ctx, err)
   969  			return err
   970  		}
   971  
   972  		buf, err = xlMeta.AppendTo(nil)
   973  		if err != nil {
   974  			logger.LogIf(ctx, err)
   975  			return err
   976  		}
   977  	}
   978  
   979  	return s.WriteAll(ctx, volume, pathJoin(path, xlStorageFormatFile), buf)
   980  }
   981  
   982  func (s *xlStorage) renameLegacyMetadata(volumeDir, path string) (err error) {
   983  	s.RLock()
   984  	legacy := s.formatLegacy
   985  	s.RUnlock()
   986  	if !legacy {
   987  		// if its not a legacy backend then this function is
   988  		// a no-op always returns errFileNotFound
   989  		return errFileNotFound
   990  	}
   991  
   992  	// Validate file path length, before reading.
   993  	filePath := pathJoin(volumeDir, path)
   994  	if err = checkPathLength(filePath); err != nil {
   995  		return err
   996  	}
   997  
   998  	srcFilePath := pathJoin(filePath, xlStorageFormatFileV1)
   999  	dstFilePath := pathJoin(filePath, xlStorageFormatFile)
  1000  
  1001  	// Renaming xl.json to xl.meta should be fully synced to disk.
  1002  	defer func() {
  1003  		if err == nil {
  1004  			if s.globalSync {
  1005  				// Sync to disk only upon success.
  1006  				globalSync()
  1007  			}
  1008  		}
  1009  	}()
  1010  
  1011  	if err = Rename(srcFilePath, dstFilePath); err != nil {
  1012  		switch {
  1013  		case isSysErrNotDir(err):
  1014  			return errFileNotFound
  1015  		case isSysErrPathNotFound(err):
  1016  			return errFileNotFound
  1017  		case isSysErrCrossDevice(err):
  1018  			return fmt.Errorf("%w (%s)->(%s)", errCrossDeviceLink, srcFilePath, dstFilePath)
  1019  		case osIsNotExist(err):
  1020  			return errFileNotFound
  1021  		case osIsExist(err):
  1022  			// This is returned only when destination is a directory and we
  1023  			// are attempting a rename from file to directory.
  1024  			return errIsNotRegular
  1025  		default:
  1026  			return err
  1027  		}
  1028  	}
  1029  	return nil
  1030  }
  1031  
  1032  // ReadVersion - reads metadata and returns FileInfo at path `xl.meta`
  1033  // for all objects less than `32KiB` this call returns data as well
  1034  // along with metadata.
  1035  func (s *xlStorage) ReadVersion(ctx context.Context, volume, path, versionID string, readData bool) (fi FileInfo, err error) {
  1036  	volumeDir, err := s.getVolDir(volume)
  1037  	if err != nil {
  1038  		return fi, err
  1039  	}
  1040  
  1041  	buf, err := s.ReadAll(ctx, volume, pathJoin(path, xlStorageFormatFile))
  1042  	if err != nil {
  1043  		if err == errFileNotFound {
  1044  			if err = s.renameLegacyMetadata(volumeDir, path); err != nil {
  1045  				if err == errFileNotFound {
  1046  					if versionID != "" {
  1047  						return fi, errFileVersionNotFound
  1048  					}
  1049  					return fi, errFileNotFound
  1050  				}
  1051  				return fi, err
  1052  			}
  1053  			buf, err = s.ReadAll(ctx, volume, pathJoin(path, xlStorageFormatFile))
  1054  			if err != nil {
  1055  				if err == errFileNotFound {
  1056  					if versionID != "" {
  1057  						return fi, errFileVersionNotFound
  1058  					}
  1059  					return fi, errFileNotFound
  1060  				}
  1061  				return fi, err
  1062  			}
  1063  		} else {
  1064  			return fi, err
  1065  		}
  1066  	}
  1067  
  1068  	if len(buf) == 0 {
  1069  		if versionID != "" {
  1070  			return fi, errFileVersionNotFound
  1071  		}
  1072  		return fi, errFileNotFound
  1073  	}
  1074  
  1075  	fi, err = getFileInfo(buf, volume, path, versionID, readData)
  1076  	if err != nil {
  1077  		return fi, err
  1078  	}
  1079  
  1080  	if readData {
  1081  		if len(fi.Data) > 0 || fi.Size == 0 {
  1082  			return fi, nil
  1083  		}
  1084  
  1085  		// Reading data for small objects when
  1086  		// - object has not yet transitioned
  1087  		// - object size lesser than 128KiB
  1088  		// - object has maximum of 1 parts
  1089  		if fi.TransitionStatus == "" && fi.DataDir != "" && fi.Size <= smallFileThreshold && len(fi.Parts) == 1 {
  1090  			// Enable O_DIRECT optionally only if drive supports it.
  1091  			requireDirectIO := globalStorageClass.GetDMA() == storageclass.DMAReadWrite
  1092  			partPath := fmt.Sprintf("part.%d", fi.Parts[0].Number)
  1093  			fi.Data, err = s.readAllData(volumeDir, pathJoin(volumeDir, path, fi.DataDir, partPath), requireDirectIO)
  1094  			if err != nil {
  1095  				return FileInfo{}, err
  1096  			}
  1097  		}
  1098  	}
  1099  
  1100  	return fi, nil
  1101  }
  1102  
  1103  func (s *xlStorage) readAllData(volumeDir string, filePath string, requireDirectIO bool) (buf []byte, err error) {
  1104  	var r io.ReadCloser
  1105  	if requireDirectIO {
  1106  		var f *os.File
  1107  		f, err = disk.OpenFileDirectIO(filePath, readMode, 0666)
  1108  		r = &odirectReader{f, nil, nil, true, true, s, nil}
  1109  	} else {
  1110  		r, err = OpenFile(filePath, readMode, 0)
  1111  	}
  1112  	if err != nil {
  1113  		if osIsNotExist(err) {
  1114  			// Check if the object doesn't exist because its bucket
  1115  			// is missing in order to return the correct error.
  1116  			if err = Access(volumeDir); err != nil && osIsNotExist(err) {
  1117  				return nil, errVolumeNotFound
  1118  			}
  1119  			return nil, errFileNotFound
  1120  		} else if osIsPermission(err) {
  1121  			return nil, errFileAccessDenied
  1122  		} else if isSysErrNotDir(err) || isSysErrIsDir(err) {
  1123  			return nil, errFileNotFound
  1124  		} else if isSysErrHandleInvalid(err) {
  1125  			// This case is special and needs to be handled for windows.
  1126  			return nil, errFileNotFound
  1127  		} else if isSysErrIO(err) {
  1128  			return nil, errFaultyDisk
  1129  		} else if isSysErrTooManyFiles(err) {
  1130  			return nil, errTooManyOpenFiles
  1131  		} else if isSysErrInvalidArg(err) {
  1132  			st, _ := Lstat(filePath)
  1133  			if st != nil && st.IsDir() {
  1134  				// Linux returns InvalidArg for directory O_DIRECT
  1135  				// we need to keep this fallback code to return correct
  1136  				// errors upwards.
  1137  				return nil, errFileNotFound
  1138  			}
  1139  			return nil, errUnsupportedDisk
  1140  		}
  1141  		return nil, err
  1142  	}
  1143  
  1144  	defer r.Close()
  1145  	buf, err = ioutil.ReadAll(r)
  1146  	if err != nil {
  1147  		err = osErrToFileErr(err)
  1148  	}
  1149  
  1150  	return buf, err
  1151  }
  1152  
  1153  // ReadAll reads from r until an error or EOF and returns the data it read.
  1154  // A successful call returns err == nil, not err == EOF. Because ReadAll is
  1155  // defined to read from src until EOF, it does not treat an EOF from Read
  1156  // as an error to be reported.
  1157  // This API is meant to be used on files which have small memory footprint, do
  1158  // not use this on large files as it would cause server to crash.
  1159  func (s *xlStorage) ReadAll(ctx context.Context, volume string, path string) (buf []byte, err error) {
  1160  	volumeDir, err := s.getVolDir(volume)
  1161  	if err != nil {
  1162  		return nil, err
  1163  	}
  1164  
  1165  	// Validate file path length, before reading.
  1166  	filePath := pathJoin(volumeDir, path)
  1167  	if err = checkPathLength(filePath); err != nil {
  1168  		return nil, err
  1169  	}
  1170  
  1171  	requireDirectIO := globalStorageClass.GetDMA() == storageclass.DMAReadWrite
  1172  	return s.readAllData(volumeDir, filePath, requireDirectIO)
  1173  }
  1174  
  1175  // ReadFile reads exactly len(buf) bytes into buf. It returns the
  1176  // number of bytes copied. The error is EOF only if no bytes were
  1177  // read. On return, n == len(buf) if and only if err == nil. n == 0
  1178  // for io.EOF.
  1179  //
  1180  // If an EOF happens after reading some but not all the bytes,
  1181  // ReadFile returns ErrUnexpectedEOF.
  1182  //
  1183  // If the BitrotVerifier is not nil or not verified ReadFile
  1184  // tries to verify whether the disk has bitrot.
  1185  //
  1186  // Additionally ReadFile also starts reading from an offset. ReadFile
  1187  // semantics are same as io.ReadFull.
  1188  func (s *xlStorage) ReadFile(ctx context.Context, volume string, path string, offset int64, buffer []byte, verifier *BitrotVerifier) (int64, error) {
  1189  	if offset < 0 {
  1190  		return 0, errInvalidArgument
  1191  	}
  1192  
  1193  	volumeDir, err := s.getVolDir(volume)
  1194  	if err != nil {
  1195  		return 0, err
  1196  	}
  1197  
  1198  	var n int
  1199  
  1200  	// Stat a volume entry.
  1201  	if err = Access(volumeDir); err != nil {
  1202  		if osIsNotExist(err) {
  1203  			return 0, errVolumeNotFound
  1204  		} else if isSysErrIO(err) {
  1205  			return 0, errFaultyDisk
  1206  		} else if osIsPermission(err) {
  1207  			return 0, errFileAccessDenied
  1208  		}
  1209  		return 0, err
  1210  	}
  1211  
  1212  	// Validate effective path length before reading.
  1213  	filePath := pathJoin(volumeDir, path)
  1214  	if err = checkPathLength(filePath); err != nil {
  1215  		return 0, err
  1216  	}
  1217  
  1218  	// Open the file for reading.
  1219  	file, err := Open(filePath)
  1220  	if err != nil {
  1221  		switch {
  1222  		case osIsNotExist(err):
  1223  			return 0, errFileNotFound
  1224  		case osIsPermission(err):
  1225  			return 0, errFileAccessDenied
  1226  		case isSysErrNotDir(err):
  1227  			return 0, errFileAccessDenied
  1228  		case isSysErrIO(err):
  1229  			return 0, errFaultyDisk
  1230  		case isSysErrTooManyFiles(err):
  1231  			return 0, errTooManyOpenFiles
  1232  		default:
  1233  			return 0, err
  1234  		}
  1235  	}
  1236  
  1237  	// Close the file descriptor.
  1238  	defer file.Close()
  1239  
  1240  	st, err := file.Stat()
  1241  	if err != nil {
  1242  		return 0, err
  1243  	}
  1244  
  1245  	// Verify it is a regular file, otherwise subsequent Seek is
  1246  	// undefined.
  1247  	if !st.Mode().IsRegular() {
  1248  		return 0, errIsNotRegular
  1249  	}
  1250  
  1251  	if verifier == nil {
  1252  		n, err = file.ReadAt(buffer, offset)
  1253  		return int64(n), err
  1254  	}
  1255  
  1256  	h := verifier.algorithm.New()
  1257  	if _, err = io.Copy(h, io.LimitReader(file, offset)); err != nil {
  1258  		return 0, err
  1259  	}
  1260  
  1261  	if n, err = io.ReadFull(file, buffer); err != nil {
  1262  		return int64(n), err
  1263  	}
  1264  
  1265  	if _, err = h.Write(buffer); err != nil {
  1266  		return 0, err
  1267  	}
  1268  
  1269  	if _, err = io.Copy(h, file); err != nil {
  1270  		return 0, err
  1271  	}
  1272  
  1273  	if !bytes.Equal(h.Sum(nil), verifier.sum) {
  1274  		return 0, errFileCorrupt
  1275  	}
  1276  
  1277  	return int64(len(buffer)), nil
  1278  }
  1279  
  1280  func (s *xlStorage) openFile(filePath string, mode int) (f *os.File, err error) {
  1281  	// Create top level directories if they don't exist.
  1282  	// with mode 0777 mkdir honors system umask.
  1283  	if err = mkdirAll(pathutil.Dir(filePath), 0777); err != nil {
  1284  		return nil, osErrToFileErr(err)
  1285  	}
  1286  
  1287  	w, err := OpenFile(filePath, mode|writeMode, 0666)
  1288  	if err != nil {
  1289  		// File path cannot be verified since one of the parents is a file.
  1290  		switch {
  1291  		case isSysErrIsDir(err):
  1292  			return nil, errIsNotRegular
  1293  		case osIsPermission(err):
  1294  			return nil, errFileAccessDenied
  1295  		case isSysErrIO(err):
  1296  			return nil, errFaultyDisk
  1297  		case isSysErrTooManyFiles(err):
  1298  			return nil, errTooManyOpenFiles
  1299  		default:
  1300  			return nil, err
  1301  		}
  1302  	}
  1303  
  1304  	return w, nil
  1305  }
  1306  
  1307  // To support O_DIRECT reads for erasure backends.
  1308  type odirectReader struct {
  1309  	f         *os.File
  1310  	buf       []byte
  1311  	bufp      *[]byte
  1312  	freshRead bool
  1313  	smallFile bool
  1314  	s         *xlStorage
  1315  	err       error
  1316  }
  1317  
  1318  // Read - Implements Reader interface.
  1319  func (o *odirectReader) Read(buf []byte) (n int, err error) {
  1320  	if o.err != nil && (len(o.buf) == 0 || o.freshRead) {
  1321  		return 0, o.err
  1322  	}
  1323  	if o.buf == nil {
  1324  		if o.smallFile {
  1325  			o.bufp = o.s.poolSmall.Get().(*[]byte)
  1326  		} else {
  1327  			o.bufp = o.s.poolLarge.Get().(*[]byte)
  1328  		}
  1329  	}
  1330  	if o.freshRead {
  1331  		o.buf = *o.bufp
  1332  		n, err = o.f.Read(o.buf)
  1333  		if err != nil && err != io.EOF {
  1334  			if isSysErrInvalidArg(err) {
  1335  				if err = disk.DisableDirectIO(o.f); err != nil {
  1336  					o.err = err
  1337  					return n, err
  1338  				}
  1339  				n, err = o.f.Read(o.buf)
  1340  			}
  1341  			if err != nil && err != io.EOF {
  1342  				o.err = err
  1343  				return n, err
  1344  			}
  1345  		}
  1346  		if n == 0 {
  1347  			// err is likely io.EOF
  1348  			o.err = err
  1349  			return n, err
  1350  		}
  1351  		o.err = err
  1352  		o.buf = o.buf[:n]
  1353  		o.freshRead = false
  1354  	}
  1355  	if len(buf) >= len(o.buf) {
  1356  		n = copy(buf, o.buf)
  1357  		o.freshRead = true
  1358  		return n, o.err
  1359  	}
  1360  	n = copy(buf, o.buf)
  1361  	o.buf = o.buf[n:]
  1362  	// There is more left in buffer, do not return any EOF yet.
  1363  	return n, nil
  1364  }
  1365  
  1366  // Close - Release the buffer and close the file.
  1367  func (o *odirectReader) Close() error {
  1368  	if o.smallFile {
  1369  		o.s.poolSmall.Put(o.bufp)
  1370  	} else {
  1371  		o.s.poolLarge.Put(o.bufp)
  1372  	}
  1373  	return o.f.Close()
  1374  }
  1375  
  1376  // ReadFileStream - Returns the read stream of the file.
  1377  func (s *xlStorage) ReadFileStream(ctx context.Context, volume, path string, offset, length int64) (io.ReadCloser, error) {
  1378  	if offset < 0 {
  1379  		return nil, errInvalidArgument
  1380  	}
  1381  
  1382  	volumeDir, err := s.getVolDir(volume)
  1383  	if err != nil {
  1384  		return nil, err
  1385  	}
  1386  
  1387  	// Validate effective path length before reading.
  1388  	filePath := pathJoin(volumeDir, path)
  1389  	if err = checkPathLength(filePath); err != nil {
  1390  		return nil, err
  1391  	}
  1392  
  1393  	var file *os.File
  1394  	// O_DIRECT only supported if offset is zero
  1395  	if offset == 0 && globalStorageClass.GetDMA() == storageclass.DMAReadWrite {
  1396  		file, err = disk.OpenFileDirectIO(filePath, readMode, 0666)
  1397  	} else {
  1398  		// Open the file for reading.
  1399  		file, err = OpenFile(filePath, readMode, 0666)
  1400  	}
  1401  	if err != nil {
  1402  		switch {
  1403  		case osIsNotExist(err):
  1404  			if err = Access(volumeDir); err != nil && osIsNotExist(err) {
  1405  				return nil, errVolumeNotFound
  1406  			}
  1407  			return nil, errFileNotFound
  1408  		case osIsPermission(err):
  1409  			return nil, errFileAccessDenied
  1410  		case isSysErrNotDir(err):
  1411  			return nil, errFileAccessDenied
  1412  		case isSysErrIO(err):
  1413  			return nil, errFaultyDisk
  1414  		case isSysErrTooManyFiles(err):
  1415  			return nil, errTooManyOpenFiles
  1416  		case isSysErrInvalidArg(err):
  1417  			return nil, errUnsupportedDisk
  1418  		default:
  1419  			return nil, err
  1420  		}
  1421  	}
  1422  
  1423  	st, err := file.Stat()
  1424  	if err != nil {
  1425  		file.Close()
  1426  		return nil, err
  1427  	}
  1428  
  1429  	// Verify it is a regular file, otherwise subsequent Seek is
  1430  	// undefined.
  1431  	if !st.Mode().IsRegular() {
  1432  		file.Close()
  1433  		return nil, errIsNotRegular
  1434  	}
  1435  
  1436  	if offset == 0 && globalStorageClass.GetDMA() == storageclass.DMAReadWrite {
  1437  		or := &odirectReader{file, nil, nil, true, false, s, nil}
  1438  		if length <= smallFileThreshold {
  1439  			or = &odirectReader{file, nil, nil, true, true, s, nil}
  1440  		}
  1441  		r := struct {
  1442  			io.Reader
  1443  			io.Closer
  1444  		}{Reader: io.LimitReader(or, length), Closer: closeWrapper(func() error {
  1445  			return or.Close()
  1446  		})}
  1447  		return r, nil
  1448  	}
  1449  
  1450  	r := struct {
  1451  		io.Reader
  1452  		io.Closer
  1453  	}{Reader: io.LimitReader(file, length), Closer: closeWrapper(func() error {
  1454  		return file.Close()
  1455  	})}
  1456  
  1457  	if offset > 0 {
  1458  		if _, err = file.Seek(offset, io.SeekStart); err != nil {
  1459  			r.Close()
  1460  			return nil, err
  1461  		}
  1462  	}
  1463  
  1464  	// Add readahead to big reads
  1465  	if length >= readAheadSize {
  1466  		rc, err := readahead.NewReadCloserSize(r, readAheadBuffers, readAheadBufSize)
  1467  		if err != nil {
  1468  			r.Close()
  1469  			return nil, err
  1470  		}
  1471  		return rc, nil
  1472  	}
  1473  
  1474  	// Just add a small 64k buffer.
  1475  	r.Reader = bufio.NewReaderSize(r.Reader, 64<<10)
  1476  	return r, nil
  1477  }
  1478  
  1479  // closeWrapper converts a function to an io.Closer
  1480  type closeWrapper func() error
  1481  
  1482  // Close calls the wrapped function.
  1483  func (c closeWrapper) Close() error {
  1484  	return c()
  1485  }
  1486  
  1487  // CreateFile - creates the file.
  1488  func (s *xlStorage) CreateFile(ctx context.Context, volume, path string, fileSize int64, r io.Reader) (err error) {
  1489  	if fileSize < -1 {
  1490  		return errInvalidArgument
  1491  	}
  1492  
  1493  	volumeDir, err := s.getVolDir(volume)
  1494  	if err != nil {
  1495  		return err
  1496  	}
  1497  
  1498  	filePath := pathJoin(volumeDir, path)
  1499  	if err = checkPathLength(filePath); err != nil {
  1500  		return err
  1501  	}
  1502  
  1503  	parentFilePath := pathutil.Dir(filePath)
  1504  	defer func() {
  1505  		if err != nil {
  1506  			if volume == minioMetaTmpBucket {
  1507  				// only cleanup parent path if the
  1508  				// parent volume name is minioMetaTmpBucket
  1509  				removeAll(parentFilePath)
  1510  			}
  1511  		}
  1512  	}()
  1513  
  1514  	if fileSize >= 0 && fileSize <= smallFileThreshold {
  1515  		// For streams smaller than 128KiB we simply write them as O_DSYNC (fdatasync)
  1516  		// and not O_DIRECT to avoid the complexities of aligned I/O.
  1517  		w, err := s.openFile(filePath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC)
  1518  		if err != nil {
  1519  			return err
  1520  		}
  1521  		defer w.Close()
  1522  
  1523  		written, err := io.Copy(w, r)
  1524  		if err != nil {
  1525  			return osErrToFileErr(err)
  1526  		}
  1527  
  1528  		if written < fileSize {
  1529  			return errLessData
  1530  		} else if written > fileSize {
  1531  			return errMoreData
  1532  		}
  1533  
  1534  		return nil
  1535  	}
  1536  
  1537  	// Create top level directories if they don't exist.
  1538  	// with mode 0777 mkdir honors system umask.
  1539  	if err = mkdirAll(parentFilePath, 0777); err != nil {
  1540  		return osErrToFileErr(err)
  1541  	}
  1542  
  1543  	w, err := OpenFileDirectIO(filePath, os.O_CREATE|os.O_WRONLY|os.O_EXCL, 0666)
  1544  	if err != nil {
  1545  		return osErrToFileErr(err)
  1546  	}
  1547  
  1548  	defer func() {
  1549  		disk.Fdatasync(w) // Only interested in flushing the size_t not mtime/atime
  1550  		w.Close()
  1551  	}()
  1552  
  1553  	bufp := s.poolLarge.Get().(*[]byte)
  1554  	defer s.poolLarge.Put(bufp)
  1555  
  1556  	written, err := xioutil.CopyAligned(w, r, *bufp, fileSize)
  1557  	if err != nil {
  1558  		return err
  1559  	}
  1560  
  1561  	if written < fileSize && fileSize >= 0 {
  1562  		return errLessData
  1563  	} else if written > fileSize && fileSize >= 0 {
  1564  		return errMoreData
  1565  	}
  1566  
  1567  	return nil
  1568  }
  1569  
  1570  func (s *xlStorage) WriteAll(ctx context.Context, volume string, path string, b []byte) (err error) {
  1571  	volumeDir, err := s.getVolDir(volume)
  1572  	if err != nil {
  1573  		return err
  1574  	}
  1575  
  1576  	filePath := pathJoin(volumeDir, path)
  1577  	if err = checkPathLength(filePath); err != nil {
  1578  		return err
  1579  	}
  1580  
  1581  	w, err := s.openFile(filePath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC)
  1582  	if err != nil {
  1583  		return err
  1584  	}
  1585  	defer w.Close()
  1586  
  1587  	n, err := w.Write(b)
  1588  	if err != nil {
  1589  		return err
  1590  	}
  1591  
  1592  	if n != len(b) {
  1593  		return io.ErrShortWrite
  1594  	}
  1595  
  1596  	return nil
  1597  }
  1598  
  1599  // AppendFile - append a byte array at path, if file doesn't exist at
  1600  // path this call explicitly creates it.
  1601  func (s *xlStorage) AppendFile(ctx context.Context, volume string, path string, buf []byte) (err error) {
  1602  	volumeDir, err := s.getVolDir(volume)
  1603  	if err != nil {
  1604  		return err
  1605  	}
  1606  
  1607  	// Stat a volume entry.
  1608  	if err = Access(volumeDir); err != nil {
  1609  		if osIsNotExist(err) {
  1610  			return errVolumeNotFound
  1611  		} else if osIsPermission(err) {
  1612  			return errVolumeAccessDenied
  1613  		} else if isSysErrIO(err) {
  1614  			return errFaultyDisk
  1615  		}
  1616  		return err
  1617  	}
  1618  
  1619  	filePath := pathJoin(volumeDir, path)
  1620  	if err = checkPathLength(filePath); err != nil {
  1621  		return err
  1622  	}
  1623  
  1624  	var w *os.File
  1625  	// Create file if not found. Not doing O_DIRECT here to avoid the code that does buffer aligned writes.
  1626  	// AppendFile() is only used by healing code to heal objects written in old format.
  1627  	w, err = s.openFile(filePath, os.O_CREATE|os.O_APPEND|os.O_WRONLY)
  1628  	if err != nil {
  1629  		return err
  1630  	}
  1631  	defer w.Close()
  1632  
  1633  	n, err := w.Write(buf)
  1634  	if err != nil {
  1635  		return err
  1636  	}
  1637  
  1638  	if n != len(buf) {
  1639  		return io.ErrShortWrite
  1640  	}
  1641  
  1642  	return nil
  1643  }
  1644  
  1645  // CheckParts check if path has necessary parts available.
  1646  func (s *xlStorage) CheckParts(ctx context.Context, volume string, path string, fi FileInfo) error {
  1647  	volumeDir, err := s.getVolDir(volume)
  1648  	if err != nil {
  1649  		return err
  1650  	}
  1651  
  1652  	// Stat a volume entry.
  1653  	if err = Access(volumeDir); err != nil {
  1654  		if osIsNotExist(err) {
  1655  			return errVolumeNotFound
  1656  		}
  1657  		return err
  1658  	}
  1659  
  1660  	for _, part := range fi.Parts {
  1661  		partPath := pathJoin(path, fi.DataDir, fmt.Sprintf("part.%d", part.Number))
  1662  		filePath := pathJoin(volumeDir, partPath)
  1663  		if err = checkPathLength(filePath); err != nil {
  1664  			return err
  1665  		}
  1666  		st, err := Lstat(filePath)
  1667  		if err != nil {
  1668  			return osErrToFileErr(err)
  1669  		}
  1670  		if st.Mode().IsDir() {
  1671  			return errFileNotFound
  1672  		}
  1673  		// Check if shard is truncated.
  1674  		if st.Size() < fi.Erasure.ShardFileSize(part.Size) {
  1675  			return errFileCorrupt
  1676  		}
  1677  	}
  1678  
  1679  	return nil
  1680  }
  1681  
  1682  // CheckFile check if path has necessary metadata.
  1683  // This function does the following check, suppose
  1684  // you are creating a metadata file at "a/b/c/d/xl.meta",
  1685  // makes sure that there is no `xl.meta` at
  1686  // - "a/b/c/"
  1687  // - "a/b/"
  1688  // - "a/"
  1689  func (s *xlStorage) CheckFile(ctx context.Context, volume string, path string) error {
  1690  	volumeDir, err := s.getVolDir(volume)
  1691  	if err != nil {
  1692  		return err
  1693  	}
  1694  	s.RLock()
  1695  	formatLegacy := s.formatLegacy
  1696  	s.RUnlock()
  1697  
  1698  	var checkFile func(p string) error
  1699  	checkFile = func(p string) error {
  1700  		if p == "." || p == SlashSeparator {
  1701  			return errPathNotFound
  1702  		}
  1703  
  1704  		filePath := pathJoin(volumeDir, p, xlStorageFormatFile)
  1705  		if err := checkPathLength(filePath); err != nil {
  1706  			return err
  1707  		}
  1708  		st, _ := Lstat(filePath)
  1709  		if st == nil {
  1710  
  1711  			if !formatLegacy {
  1712  				return errPathNotFound
  1713  			}
  1714  
  1715  			filePathOld := pathJoin(volumeDir, p, xlStorageFormatFileV1)
  1716  			if err := checkPathLength(filePathOld); err != nil {
  1717  				return err
  1718  			}
  1719  
  1720  			st, _ = Lstat(filePathOld)
  1721  			if st == nil {
  1722  				return errPathNotFound
  1723  			}
  1724  		}
  1725  
  1726  		if st != nil {
  1727  			if !st.Mode().IsRegular() {
  1728  				// not a regular file return error.
  1729  				return errFileNotFound
  1730  			}
  1731  			// Success fully found
  1732  			return nil
  1733  		}
  1734  
  1735  		return checkFile(pathutil.Dir(p))
  1736  	}
  1737  
  1738  	return checkFile(path)
  1739  }
  1740  
  1741  // deleteFile deletes a file or a directory if its empty unless recursive
  1742  // is set to true. If the target is successfully deleted, it will recursively
  1743  // move up the tree, deleting empty parent directories until it finds one
  1744  // with files in it. Returns nil for a non-empty directory even when
  1745  // recursive is set to false.
  1746  func (s *xlStorage) deleteFile(basePath, deletePath string, recursive bool) error {
  1747  	if basePath == "" || deletePath == "" {
  1748  		return nil
  1749  	}
  1750  	isObjectDir := HasSuffix(deletePath, SlashSeparator)
  1751  	basePath = pathutil.Clean(basePath)
  1752  	deletePath = pathutil.Clean(deletePath)
  1753  	if !strings.HasPrefix(deletePath, basePath) || deletePath == basePath {
  1754  		return nil
  1755  	}
  1756  
  1757  	var err error
  1758  	if recursive {
  1759  		err = renameAll(deletePath, pathutil.Join(s.diskPath, minioMetaTmpDeletedBucket, mustGetUUID()))
  1760  	} else {
  1761  		err = Remove(deletePath)
  1762  	}
  1763  	if err != nil {
  1764  		switch {
  1765  		case isSysErrNotEmpty(err):
  1766  			// if object is a directory, but if its not empty
  1767  			// return FileNotFound to indicate its an empty prefix.
  1768  			if isObjectDir {
  1769  				return errFileNotFound
  1770  			}
  1771  			// Ignore errors if the directory is not empty. The server relies on
  1772  			// this functionality, and sometimes uses recursion that should not
  1773  			// error on parent directories.
  1774  			return nil
  1775  		case osIsNotExist(err):
  1776  			return errFileNotFound
  1777  		case osIsPermission(err):
  1778  			return errFileAccessDenied
  1779  		case isSysErrIO(err):
  1780  			return errFaultyDisk
  1781  		default:
  1782  			return err
  1783  		}
  1784  	}
  1785  
  1786  	deletePath = pathutil.Dir(deletePath)
  1787  
  1788  	// Delete parent directory obviously not recursively. Errors for
  1789  	// parent directories shouldn't trickle down.
  1790  	s.deleteFile(basePath, deletePath, false)
  1791  
  1792  	return nil
  1793  }
  1794  
  1795  // DeleteFile - delete a file at path.
  1796  func (s *xlStorage) Delete(ctx context.Context, volume string, path string, recursive bool) (err error) {
  1797  	volumeDir, err := s.getVolDir(volume)
  1798  	if err != nil {
  1799  		return err
  1800  	}
  1801  
  1802  	// Stat a volume entry.
  1803  	if err = Access(volumeDir); err != nil {
  1804  		if osIsNotExist(err) {
  1805  			return errVolumeNotFound
  1806  		} else if osIsPermission(err) {
  1807  			return errVolumeAccessDenied
  1808  		} else if isSysErrIO(err) {
  1809  			return errFaultyDisk
  1810  		}
  1811  		return err
  1812  	}
  1813  
  1814  	// Following code is needed so that we retain SlashSeparator suffix if any in
  1815  	// path argument.
  1816  	filePath := pathJoin(volumeDir, path)
  1817  	if err = checkPathLength(filePath); err != nil {
  1818  		return err
  1819  	}
  1820  
  1821  	// Delete file and delete parent directory as well if it's empty.
  1822  	return s.deleteFile(volumeDir, filePath, recursive)
  1823  }
  1824  
  1825  // RenameData - rename source path to destination path atomically, metadata and data directory.
  1826  func (s *xlStorage) RenameData(ctx context.Context, srcVolume, srcPath string, fi FileInfo, dstVolume, dstPath string) (err error) {
  1827  	defer func() {
  1828  		if err == nil {
  1829  			if s.globalSync {
  1830  				globalSync()
  1831  			}
  1832  		}
  1833  	}()
  1834  
  1835  	srcVolumeDir, err := s.getVolDir(srcVolume)
  1836  	if err != nil {
  1837  		return err
  1838  	}
  1839  
  1840  	dstVolumeDir, err := s.getVolDir(dstVolume)
  1841  	if err != nil {
  1842  		return err
  1843  	}
  1844  
  1845  	// Stat a volume entry.
  1846  	if err = Access(srcVolumeDir); err != nil {
  1847  		if osIsNotExist(err) {
  1848  			return errVolumeNotFound
  1849  		} else if isSysErrIO(err) {
  1850  			return errFaultyDisk
  1851  		}
  1852  		return err
  1853  	}
  1854  
  1855  	if err = Access(dstVolumeDir); err != nil {
  1856  		if osIsNotExist(err) {
  1857  			return errVolumeNotFound
  1858  		} else if isSysErrIO(err) {
  1859  			return errFaultyDisk
  1860  		}
  1861  		return err
  1862  	}
  1863  
  1864  	srcFilePath := pathutil.Join(srcVolumeDir, pathJoin(srcPath, xlStorageFormatFile))
  1865  	dstFilePath := pathutil.Join(dstVolumeDir, pathJoin(dstPath, xlStorageFormatFile))
  1866  
  1867  	var srcDataPath string
  1868  	var dstDataPath string
  1869  	dataDir := retainSlash(fi.DataDir)
  1870  	if dataDir != "" {
  1871  		srcDataPath = retainSlash(pathJoin(srcVolumeDir, srcPath, dataDir))
  1872  		// make sure to always use path.Join here, do not use pathJoin as
  1873  		// it would additionally add `/` at the end and it comes in the
  1874  		// way of renameAll(), parentDir creation.
  1875  		dstDataPath = pathutil.Join(dstVolumeDir, dstPath, dataDir)
  1876  	}
  1877  
  1878  	if err = checkPathLength(srcFilePath); err != nil {
  1879  		return err
  1880  	}
  1881  
  1882  	if err = checkPathLength(dstFilePath); err != nil {
  1883  		return err
  1884  	}
  1885  
  1886  	dstBuf, err := xioutil.ReadFile(dstFilePath)
  1887  	if err != nil {
  1888  		if !osIsNotExist(err) {
  1889  			return osErrToFileErr(err)
  1890  		}
  1891  		// errFileNotFound comes here.
  1892  		err = s.renameLegacyMetadata(dstVolumeDir, dstPath)
  1893  		if err != nil && err != errFileNotFound {
  1894  			return err
  1895  		}
  1896  		if err == nil {
  1897  			dstBuf, err = xioutil.ReadFile(dstFilePath)
  1898  			if err != nil && !osIsNotExist(err) {
  1899  				return osErrToFileErr(err)
  1900  			}
  1901  		}
  1902  		if err == errFileNotFound {
  1903  			// Verification to ensure that we
  1904  			// don't have objects already created
  1905  			// at this location, verify that resultant
  1906  			// directories don't have any unexpected
  1907  			// directories that we do not understand
  1908  			// or expect. If its already there we should
  1909  			// make sure to reject further renames
  1910  			// for such objects.
  1911  			//
  1912  			// This elaborate check is necessary to avoid
  1913  			// scenarios such as these.
  1914  			//
  1915  			// bucket1/name1/obj1/xl.meta
  1916  			// bucket1/name1/xl.meta --> this should never
  1917  			// be allowed.
  1918  			{
  1919  				entries, err := readDirN(pathutil.Dir(dstFilePath), 1)
  1920  				if err != nil && err != errFileNotFound {
  1921  					return err
  1922  				}
  1923  				if len(entries) > 0 {
  1924  					entry := pathutil.Clean(entries[0])
  1925  					if entry != legacyDataDir {
  1926  						_, uerr := uuid.Parse(entry)
  1927  						if uerr != nil {
  1928  							return errFileParentIsFile
  1929  						}
  1930  					}
  1931  				}
  1932  			}
  1933  		}
  1934  	}
  1935  
  1936  	var xlMeta xlMetaV2
  1937  	var legacyPreserved bool
  1938  	if len(dstBuf) > 0 {
  1939  		if isXL2V1Format(dstBuf) {
  1940  			if err = xlMeta.Load(dstBuf); err != nil {
  1941  				logger.LogIf(s.ctx, err)
  1942  				return err
  1943  			}
  1944  		} else {
  1945  			// This code-path is to preserve the legacy data.
  1946  			xlMetaLegacy := &xlMetaV1Object{}
  1947  			var json = jsoniter.ConfigCompatibleWithStandardLibrary
  1948  			if err := json.Unmarshal(dstBuf, xlMetaLegacy); err != nil {
  1949  				logger.LogIf(s.ctx, err)
  1950  				return errFileCorrupt
  1951  			}
  1952  			if err = xlMeta.AddLegacy(xlMetaLegacy); err != nil {
  1953  				logger.LogIf(s.ctx, err)
  1954  				return errFileCorrupt
  1955  			}
  1956  			legacyPreserved = true
  1957  		}
  1958  	} else {
  1959  		s.RLock()
  1960  		formatLegacy := s.formatLegacy
  1961  		s.RUnlock()
  1962  		// It is possible that some drives may not have `xl.meta` file
  1963  		// in such scenarios verify if atleast `part.1` files exist
  1964  		// to verify for legacy version.
  1965  		if formatLegacy {
  1966  			// We only need this code if we are moving
  1967  			// from `xl.json` to `xl.meta`, we can avoid
  1968  			// one extra readdir operation here for all
  1969  			// new deployments.
  1970  			currentDataPath := pathJoin(dstVolumeDir, dstPath)
  1971  			entries, err := readDirN(currentDataPath, 1)
  1972  			if err != nil && err != errFileNotFound {
  1973  				return osErrToFileErr(err)
  1974  			}
  1975  			for _, entry := range entries {
  1976  				if entry == xlStorageFormatFile || strings.HasSuffix(entry, slashSeparator) {
  1977  					continue
  1978  				}
  1979  				if strings.HasPrefix(entry, "part.") {
  1980  					legacyPreserved = true
  1981  					break
  1982  				}
  1983  			}
  1984  		}
  1985  	}
  1986  
  1987  	if legacyPreserved {
  1988  		// Preserve all the legacy data, could be slow, but at max there can be 10,000 parts.
  1989  		currentDataPath := pathJoin(dstVolumeDir, dstPath)
  1990  		entries, err := readDir(currentDataPath)
  1991  		if err != nil {
  1992  			return osErrToFileErr(err)
  1993  		}
  1994  
  1995  		legacyDataPath := pathJoin(dstVolumeDir, dstPath, legacyDataDir)
  1996  		// legacy data dir means its old content, honor system umask.
  1997  		if err = reliableMkdirAll(legacyDataPath, 0777); err != nil {
  1998  			return osErrToFileErr(err)
  1999  		}
  2000  
  2001  		for _, entry := range entries {
  2002  			// Skip xl.meta renames further, also ignore any directories such as `legacyDataDir`
  2003  			if entry == xlStorageFormatFile || strings.HasSuffix(entry, slashSeparator) {
  2004  				continue
  2005  			}
  2006  
  2007  			if err = Rename(pathJoin(currentDataPath, entry), pathJoin(legacyDataPath, entry)); err != nil {
  2008  				return osErrToFileErr(err)
  2009  			}
  2010  		}
  2011  	}
  2012  
  2013  	var oldDstDataPath string
  2014  	if fi.VersionID == "" {
  2015  		// return the latest "null" versionId info
  2016  		ofi, err := xlMeta.ToFileInfo(dstVolume, dstPath, nullVersionID)
  2017  		if err == nil && !ofi.Deleted {
  2018  			if xlMeta.SharedDataDirCountStr(nullVersionID, ofi.DataDir) == 0 {
  2019  				// Purge the destination path as we are not preserving anything
  2020  				// versioned object was not requested.
  2021  				oldDstDataPath = pathJoin(dstVolumeDir, dstPath, ofi.DataDir)
  2022  			}
  2023  		}
  2024  	}
  2025  
  2026  	if err = xlMeta.AddVersion(fi); err != nil {
  2027  		return err
  2028  	}
  2029  
  2030  	dstBuf, err = xlMeta.AppendTo(nil)
  2031  	if err != nil {
  2032  		logger.LogIf(ctx, err)
  2033  		return errFileCorrupt
  2034  	}
  2035  
  2036  	if srcDataPath != "" {
  2037  		if err = s.WriteAll(ctx, srcVolume, pathJoin(srcPath, xlStorageFormatFile), dstBuf); err != nil {
  2038  			return err
  2039  		}
  2040  
  2041  		if oldDstDataPath != "" {
  2042  			renameAll(oldDstDataPath, pathutil.Join(s.diskPath, minioMetaTmpDeletedBucket, mustGetUUID()))
  2043  		}
  2044  
  2045  		// renameAll only for objects that have xl.meta not saved inline.
  2046  		if len(fi.Data) == 0 && fi.Size > 0 {
  2047  			renameAll(dstDataPath, pathutil.Join(s.diskPath, minioMetaTmpDeletedBucket, mustGetUUID()))
  2048  			if err = renameAll(srcDataPath, dstDataPath); err != nil {
  2049  				logger.LogIf(ctx, err)
  2050  				return osErrToFileErr(err)
  2051  			}
  2052  		}
  2053  
  2054  		// Commit meta-file
  2055  		if err = renameAll(srcFilePath, dstFilePath); err != nil {
  2056  			logger.LogIf(ctx, err)
  2057  			return osErrToFileErr(err)
  2058  		}
  2059  	} else {
  2060  		// Write meta-file directly, no data
  2061  		if err = s.WriteAll(ctx, dstVolume, pathJoin(dstPath, xlStorageFormatFile), dstBuf); err != nil {
  2062  			logger.LogIf(ctx, err)
  2063  			return err
  2064  		}
  2065  	}
  2066  
  2067  	// Remove parent dir of the source file if empty
  2068  	parentDir := pathutil.Dir(srcFilePath)
  2069  	s.deleteFile(srcVolumeDir, parentDir, false)
  2070  	return nil
  2071  }
  2072  
  2073  // RenameFile - rename source path to destination path atomically.
  2074  func (s *xlStorage) RenameFile(ctx context.Context, srcVolume, srcPath, dstVolume, dstPath string) (err error) {
  2075  	srcVolumeDir, err := s.getVolDir(srcVolume)
  2076  	if err != nil {
  2077  		return err
  2078  	}
  2079  	dstVolumeDir, err := s.getVolDir(dstVolume)
  2080  	if err != nil {
  2081  		return err
  2082  	}
  2083  	// Stat a volume entry.
  2084  	if err = Access(srcVolumeDir); err != nil {
  2085  		if osIsNotExist(err) {
  2086  			return errVolumeNotFound
  2087  		} else if isSysErrIO(err) {
  2088  			return errFaultyDisk
  2089  		}
  2090  		return err
  2091  	}
  2092  
  2093  	if err = Access(dstVolumeDir); err != nil {
  2094  		if osIsNotExist(err) {
  2095  			return errVolumeNotFound
  2096  		} else if isSysErrIO(err) {
  2097  			return errFaultyDisk
  2098  		}
  2099  		return err
  2100  	}
  2101  
  2102  	srcIsDir := HasSuffix(srcPath, SlashSeparator)
  2103  	dstIsDir := HasSuffix(dstPath, SlashSeparator)
  2104  	// Either src and dst have to be directories or files, else return error.
  2105  	if !(srcIsDir && dstIsDir || !srcIsDir && !dstIsDir) {
  2106  		return errFileAccessDenied
  2107  	}
  2108  	srcFilePath := pathutil.Join(srcVolumeDir, srcPath)
  2109  	if err = checkPathLength(srcFilePath); err != nil {
  2110  		return err
  2111  	}
  2112  	dstFilePath := pathutil.Join(dstVolumeDir, dstPath)
  2113  	if err = checkPathLength(dstFilePath); err != nil {
  2114  		return err
  2115  	}
  2116  	if srcIsDir {
  2117  		// If source is a directory, we expect the destination to be non-existent but we
  2118  		// we still need to allow overwriting an empty directory since it represents
  2119  		// an object empty directory.
  2120  		dirInfo, err := Lstat(dstFilePath)
  2121  		if isSysErrIO(err) {
  2122  			return errFaultyDisk
  2123  		}
  2124  		if err != nil {
  2125  			if !osIsNotExist(err) {
  2126  				return err
  2127  			}
  2128  		} else {
  2129  			if !dirInfo.IsDir() {
  2130  				return errFileAccessDenied
  2131  			}
  2132  			if err = Remove(dstFilePath); err != nil {
  2133  				if isSysErrNotEmpty(err) {
  2134  					return errFileAccessDenied
  2135  				}
  2136  				return err
  2137  			}
  2138  		}
  2139  	}
  2140  
  2141  	if err = renameAll(srcFilePath, dstFilePath); err != nil {
  2142  		return osErrToFileErr(err)
  2143  	}
  2144  
  2145  	// Remove parent dir of the source file if empty
  2146  	parentDir := pathutil.Dir(srcFilePath)
  2147  	s.deleteFile(srcVolumeDir, parentDir, false)
  2148  
  2149  	return nil
  2150  }
  2151  
  2152  func (s *xlStorage) bitrotVerify(partPath string, partSize int64, algo BitrotAlgorithm, sum []byte, shardSize int64) error {
  2153  	// Open the file for reading.
  2154  	file, err := Open(partPath)
  2155  	if err != nil {
  2156  		return osErrToFileErr(err)
  2157  	}
  2158  
  2159  	// Close the file descriptor.
  2160  	defer file.Close()
  2161  	fi, err := file.Stat()
  2162  	if err != nil {
  2163  		// Unable to stat on the file, return an expected error
  2164  		// for healing code to fix this file.
  2165  		return err
  2166  	}
  2167  	return bitrotVerify(file, fi.Size(), partSize, algo, sum, shardSize)
  2168  }
  2169  
  2170  func (s *xlStorage) VerifyFile(ctx context.Context, volume, path string, fi FileInfo) (err error) {
  2171  	volumeDir, err := s.getVolDir(volume)
  2172  	if err != nil {
  2173  		return err
  2174  	}
  2175  
  2176  	// Stat a volume entry.
  2177  	if err = Access(volumeDir); err != nil {
  2178  		if osIsNotExist(err) {
  2179  			return errVolumeNotFound
  2180  		} else if isSysErrIO(err) {
  2181  			return errFaultyDisk
  2182  		} else if osIsPermission(err) {
  2183  			return errVolumeAccessDenied
  2184  		}
  2185  		return err
  2186  	}
  2187  
  2188  	erasure := fi.Erasure
  2189  	for _, part := range fi.Parts {
  2190  		checksumInfo := erasure.GetChecksumInfo(part.Number)
  2191  		partPath := pathJoin(volumeDir, path, fi.DataDir, fmt.Sprintf("part.%d", part.Number))
  2192  		if err := s.bitrotVerify(partPath,
  2193  			erasure.ShardFileSize(part.Size),
  2194  			checksumInfo.Algorithm,
  2195  			checksumInfo.Hash, erasure.ShardSize()); err != nil {
  2196  			if !IsErr(err, []error{
  2197  				errFileNotFound,
  2198  				errVolumeNotFound,
  2199  				errFileCorrupt,
  2200  			}...) {
  2201  				logger.GetReqInfo(s.ctx).AppendTags("disk", s.String())
  2202  				logger.LogIf(s.ctx, err)
  2203  			}
  2204  			return err
  2205  		}
  2206  	}
  2207  
  2208  	return nil
  2209  }