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

     1  /*
     2   * MinIO Cloud Storage, (C) 2018 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  	"encoding/binary"
    22  	"encoding/gob"
    23  	"encoding/hex"
    24  	"errors"
    25  	"fmt"
    26  	"io"
    27  	"io/ioutil"
    28  	"net/http"
    29  	"os/user"
    30  	"path"
    31  	"strconv"
    32  	"strings"
    33  	"time"
    34  
    35  	"github.com/tinylib/msgp/msgp"
    36  
    37  	jwtreq "github.com/dgrijalva/jwt-go/request"
    38  	"github.com/gorilla/mux"
    39  
    40  	"storj.io/minio/cmd/config"
    41  	xhttp "storj.io/minio/cmd/http"
    42  	xjwt "storj.io/minio/cmd/jwt"
    43  	"storj.io/minio/cmd/logger"
    44  	xnet "storj.io/minio/pkg/net"
    45  )
    46  
    47  var errDiskStale = errors.New("disk stale")
    48  
    49  // To abstract a disk over network.
    50  type storageRESTServer struct {
    51  	storage *xlStorage
    52  }
    53  
    54  func (s *storageRESTServer) WriteErrorResponse(w http.ResponseWriter, err error) {
    55  	if errors.Is(err, errDiskStale) {
    56  		w.WriteHeader(http.StatusPreconditionFailed)
    57  	} else {
    58  		w.WriteHeader(http.StatusForbidden)
    59  	}
    60  	w.Write([]byte(err.Error()))
    61  	w.(http.Flusher).Flush()
    62  }
    63  
    64  // DefaultSkewTime - skew time is 15 minutes between minio peers.
    65  const DefaultSkewTime = 15 * time.Minute
    66  
    67  // Authenticates storage client's requests and validates for skewed time.
    68  func storageServerRequestValidate(r *http.Request) error {
    69  	token, err := jwtreq.AuthorizationHeaderExtractor.ExtractToken(r)
    70  	if err != nil {
    71  		if err == jwtreq.ErrNoTokenInRequest {
    72  			return errNoAuthToken
    73  		}
    74  		return err
    75  	}
    76  
    77  	claims := xjwt.NewStandardClaims()
    78  	if err = xjwt.ParseWithStandardClaims(token, claims, []byte(globalActiveCred.SecretKey)); err != nil {
    79  		return errAuthentication
    80  	}
    81  
    82  	owner := claims.AccessKey == globalActiveCred.AccessKey || claims.Subject == globalActiveCred.AccessKey
    83  	if !owner {
    84  		return errAuthentication
    85  	}
    86  
    87  	if claims.Audience != r.URL.RawQuery {
    88  		return errAuthentication
    89  	}
    90  
    91  	requestTimeStr := r.Header.Get("X-Minio-Time")
    92  	requestTime, err := time.Parse(time.RFC3339, requestTimeStr)
    93  	if err != nil {
    94  		return err
    95  	}
    96  	utcNow := UTCNow()
    97  	delta := requestTime.Sub(utcNow)
    98  	if delta < 0 {
    99  		delta *= -1
   100  	}
   101  	if delta > DefaultSkewTime {
   102  		return fmt.Errorf("client time %v is too apart with server time %v", requestTime, utcNow)
   103  	}
   104  
   105  	return nil
   106  }
   107  
   108  // IsValid - To authenticate and verify the time difference.
   109  func (s *storageRESTServer) IsValid(w http.ResponseWriter, r *http.Request) bool {
   110  	if s.storage == nil {
   111  		s.WriteErrorResponse(w, errDiskNotFound)
   112  		return false
   113  	}
   114  
   115  	if err := storageServerRequestValidate(r); err != nil {
   116  		s.WriteErrorResponse(w, err)
   117  		return false
   118  	}
   119  
   120  	diskID := r.URL.Query().Get(storageRESTDiskID)
   121  	if diskID == "" {
   122  		// Request sent empty disk-id, we allow the request
   123  		// as the peer might be coming up and trying to read format.json
   124  		// or create format.json
   125  		return true
   126  	}
   127  
   128  	storedDiskID, err := s.storage.GetDiskID()
   129  	if err != nil {
   130  		s.WriteErrorResponse(w, err)
   131  		return false
   132  	}
   133  
   134  	if diskID != storedDiskID {
   135  		s.WriteErrorResponse(w, errDiskStale)
   136  		return false
   137  	}
   138  
   139  	// If format.json is available and request sent the right disk-id, we allow the request
   140  	return true
   141  }
   142  
   143  // HealthHandler handler checks if disk is stale
   144  func (s *storageRESTServer) HealthHandler(w http.ResponseWriter, r *http.Request) {
   145  	s.IsValid(w, r)
   146  }
   147  
   148  // DiskInfoHandler - returns disk info.
   149  func (s *storageRESTServer) DiskInfoHandler(w http.ResponseWriter, r *http.Request) {
   150  	if !s.IsValid(w, r) {
   151  		return
   152  	}
   153  	info, err := s.storage.DiskInfo(r.Context())
   154  	if err != nil {
   155  		info.Error = err.Error()
   156  	}
   157  	defer w.(http.Flusher).Flush()
   158  	logger.LogIf(r.Context(), msgp.Encode(w, &info))
   159  }
   160  
   161  func (s *storageRESTServer) NSScannerHandler(w http.ResponseWriter, r *http.Request) {
   162  	if !s.IsValid(w, r) {
   163  		return
   164  	}
   165  
   166  	setEventStreamHeaders(w)
   167  
   168  	var cache dataUsageCache
   169  	err := cache.deserialize(r.Body)
   170  	if err != nil {
   171  		logger.LogIf(r.Context(), err)
   172  		s.WriteErrorResponse(w, err)
   173  		return
   174  	}
   175  
   176  	resp := streamHTTPResponse(w)
   177  	usageInfo, err := s.storage.NSScanner(r.Context(), cache)
   178  	if err != nil {
   179  		resp.CloseWithError(err)
   180  		return
   181  	}
   182  	resp.CloseWithError(usageInfo.serializeTo(resp))
   183  }
   184  
   185  // MakeVolHandler - make a volume.
   186  func (s *storageRESTServer) MakeVolHandler(w http.ResponseWriter, r *http.Request) {
   187  	if !s.IsValid(w, r) {
   188  		return
   189  	}
   190  	vars := mux.Vars(r)
   191  	volume := vars[storageRESTVolume]
   192  	err := s.storage.MakeVol(r.Context(), volume)
   193  	if err != nil {
   194  		s.WriteErrorResponse(w, err)
   195  	}
   196  }
   197  
   198  // MakeVolBulkHandler - create multiple volumes as a bulk operation.
   199  func (s *storageRESTServer) MakeVolBulkHandler(w http.ResponseWriter, r *http.Request) {
   200  	if !s.IsValid(w, r) {
   201  		return
   202  	}
   203  	vars := mux.Vars(r)
   204  	volumes := strings.Split(vars[storageRESTVolumes], ",")
   205  	err := s.storage.MakeVolBulk(r.Context(), volumes...)
   206  	if err != nil {
   207  		s.WriteErrorResponse(w, err)
   208  	}
   209  }
   210  
   211  // ListVolsHandler - list volumes.
   212  func (s *storageRESTServer) ListVolsHandler(w http.ResponseWriter, r *http.Request) {
   213  	if !s.IsValid(w, r) {
   214  		return
   215  	}
   216  	infos, err := s.storage.ListVols(r.Context())
   217  	if err != nil {
   218  		s.WriteErrorResponse(w, err)
   219  		return
   220  	}
   221  	defer w.(http.Flusher).Flush()
   222  	logger.LogIf(r.Context(), msgp.Encode(w, VolsInfo(infos)))
   223  }
   224  
   225  // StatVolHandler - stat a volume.
   226  func (s *storageRESTServer) StatVolHandler(w http.ResponseWriter, r *http.Request) {
   227  	if !s.IsValid(w, r) {
   228  		return
   229  	}
   230  	vars := mux.Vars(r)
   231  	volume := vars[storageRESTVolume]
   232  	info, err := s.storage.StatVol(r.Context(), volume)
   233  	if err != nil {
   234  		s.WriteErrorResponse(w, err)
   235  		return
   236  	}
   237  	defer w.(http.Flusher).Flush()
   238  	logger.LogIf(r.Context(), msgp.Encode(w, &info))
   239  }
   240  
   241  // DeleteVolumeHandler - delete a volume.
   242  func (s *storageRESTServer) DeleteVolHandler(w http.ResponseWriter, r *http.Request) {
   243  	if !s.IsValid(w, r) {
   244  		return
   245  	}
   246  	vars := mux.Vars(r)
   247  	volume := vars[storageRESTVolume]
   248  	forceDelete := vars[storageRESTForceDelete] == "true"
   249  	err := s.storage.DeleteVol(r.Context(), volume, forceDelete)
   250  	if err != nil {
   251  		s.WriteErrorResponse(w, err)
   252  	}
   253  }
   254  
   255  // AppendFileHandler - append data from the request to the file specified.
   256  func (s *storageRESTServer) AppendFileHandler(w http.ResponseWriter, r *http.Request) {
   257  	if !s.IsValid(w, r) {
   258  		return
   259  	}
   260  	vars := mux.Vars(r)
   261  	volume := vars[storageRESTVolume]
   262  	filePath := vars[storageRESTFilePath]
   263  
   264  	buf := make([]byte, r.ContentLength)
   265  	_, err := io.ReadFull(r.Body, buf)
   266  	if err != nil {
   267  		s.WriteErrorResponse(w, err)
   268  		return
   269  	}
   270  	err = s.storage.AppendFile(r.Context(), volume, filePath, buf)
   271  	if err != nil {
   272  		s.WriteErrorResponse(w, err)
   273  	}
   274  }
   275  
   276  // CreateFileHandler - fallocate() space for a file and copy the contents from the request.
   277  func (s *storageRESTServer) CreateFileHandler(w http.ResponseWriter, r *http.Request) {
   278  	if !s.IsValid(w, r) {
   279  		return
   280  	}
   281  	vars := mux.Vars(r)
   282  	volume := vars[storageRESTVolume]
   283  	filePath := vars[storageRESTFilePath]
   284  
   285  	fileSizeStr := vars[storageRESTLength]
   286  	fileSize, err := strconv.Atoi(fileSizeStr)
   287  	if err != nil {
   288  		s.WriteErrorResponse(w, err)
   289  		return
   290  	}
   291  
   292  	done := keepHTTPResponseAlive(w)
   293  	done(s.storage.CreateFile(r.Context(), volume, filePath, int64(fileSize), r.Body))
   294  }
   295  
   296  // DeleteVersion delete updated metadata.
   297  func (s *storageRESTServer) DeleteVersionHandler(w http.ResponseWriter, r *http.Request) {
   298  	if !s.IsValid(w, r) {
   299  		return
   300  	}
   301  	vars := mux.Vars(r)
   302  	volume := vars[storageRESTVolume]
   303  	filePath := vars[storageRESTFilePath]
   304  	forceDelMarker, err := strconv.ParseBool(vars[storageRESTForceDelMarker])
   305  	if err != nil {
   306  		s.WriteErrorResponse(w, errInvalidArgument)
   307  		return
   308  	}
   309  
   310  	if r.ContentLength < 0 {
   311  		s.WriteErrorResponse(w, errInvalidArgument)
   312  		return
   313  	}
   314  
   315  	var fi FileInfo
   316  	if err := msgp.Decode(r.Body, &fi); err != nil {
   317  		s.WriteErrorResponse(w, err)
   318  		return
   319  	}
   320  
   321  	err = s.storage.DeleteVersion(r.Context(), volume, filePath, fi, forceDelMarker)
   322  	if err != nil {
   323  		s.WriteErrorResponse(w, err)
   324  	}
   325  }
   326  
   327  // ReadVersion read metadata of versionID
   328  func (s *storageRESTServer) ReadVersionHandler(w http.ResponseWriter, r *http.Request) {
   329  	if !s.IsValid(w, r) {
   330  		return
   331  	}
   332  	vars := mux.Vars(r)
   333  	volume := vars[storageRESTVolume]
   334  	filePath := vars[storageRESTFilePath]
   335  	versionID := vars[storageRESTVersionID]
   336  	readData, err := strconv.ParseBool(vars[storageRESTReadData])
   337  	if err != nil {
   338  		s.WriteErrorResponse(w, err)
   339  		return
   340  	}
   341  
   342  	fi, err := s.storage.ReadVersion(r.Context(), volume, filePath, versionID, readData)
   343  	if err != nil {
   344  		s.WriteErrorResponse(w, err)
   345  		return
   346  	}
   347  
   348  	logger.LogIf(r.Context(), msgp.Encode(w, &fi))
   349  }
   350  
   351  // WriteMetadata write new updated metadata.
   352  func (s *storageRESTServer) WriteMetadataHandler(w http.ResponseWriter, r *http.Request) {
   353  	if !s.IsValid(w, r) {
   354  		return
   355  	}
   356  	vars := mux.Vars(r)
   357  	volume := vars[storageRESTVolume]
   358  	filePath := vars[storageRESTFilePath]
   359  
   360  	if r.ContentLength < 0 {
   361  		s.WriteErrorResponse(w, errInvalidArgument)
   362  		return
   363  	}
   364  
   365  	var fi FileInfo
   366  	if err := msgp.Decode(r.Body, &fi); err != nil {
   367  		s.WriteErrorResponse(w, err)
   368  		return
   369  	}
   370  
   371  	err := s.storage.WriteMetadata(r.Context(), volume, filePath, fi)
   372  	if err != nil {
   373  		s.WriteErrorResponse(w, err)
   374  	}
   375  }
   376  
   377  // UpdateMetadata update new updated metadata.
   378  func (s *storageRESTServer) UpdateMetadataHandler(w http.ResponseWriter, r *http.Request) {
   379  	if !s.IsValid(w, r) {
   380  		return
   381  	}
   382  	vars := mux.Vars(r)
   383  	volume := vars[storageRESTVolume]
   384  	filePath := vars[storageRESTFilePath]
   385  
   386  	if r.ContentLength < 0 {
   387  		s.WriteErrorResponse(w, errInvalidArgument)
   388  		return
   389  	}
   390  
   391  	var fi FileInfo
   392  	if err := msgp.Decode(r.Body, &fi); err != nil {
   393  		s.WriteErrorResponse(w, err)
   394  		return
   395  	}
   396  
   397  	err := s.storage.UpdateMetadata(r.Context(), volume, filePath, fi)
   398  	if err != nil {
   399  		s.WriteErrorResponse(w, err)
   400  	}
   401  }
   402  
   403  // WriteAllHandler - write to file all content.
   404  func (s *storageRESTServer) WriteAllHandler(w http.ResponseWriter, r *http.Request) {
   405  	if !s.IsValid(w, r) {
   406  		return
   407  	}
   408  	vars := mux.Vars(r)
   409  	volume := vars[storageRESTVolume]
   410  	filePath := vars[storageRESTFilePath]
   411  
   412  	if r.ContentLength < 0 {
   413  		s.WriteErrorResponse(w, errInvalidArgument)
   414  		return
   415  	}
   416  	tmp := make([]byte, r.ContentLength)
   417  	_, err := io.ReadFull(r.Body, tmp)
   418  	if err != nil {
   419  		s.WriteErrorResponse(w, err)
   420  		return
   421  	}
   422  	err = s.storage.WriteAll(r.Context(), volume, filePath, tmp)
   423  	if err != nil {
   424  		s.WriteErrorResponse(w, err)
   425  	}
   426  }
   427  
   428  // CheckPartsHandler - check if a file metadata exists.
   429  func (s *storageRESTServer) CheckPartsHandler(w http.ResponseWriter, r *http.Request) {
   430  	if !s.IsValid(w, r) {
   431  		return
   432  	}
   433  	vars := mux.Vars(r)
   434  	volume := vars[storageRESTVolume]
   435  	filePath := vars[storageRESTFilePath]
   436  
   437  	if r.ContentLength < 0 {
   438  		s.WriteErrorResponse(w, errInvalidArgument)
   439  		return
   440  	}
   441  
   442  	var fi FileInfo
   443  	if err := msgp.Decode(r.Body, &fi); err != nil {
   444  		s.WriteErrorResponse(w, err)
   445  		return
   446  	}
   447  
   448  	if err := s.storage.CheckParts(r.Context(), volume, filePath, fi); err != nil {
   449  		s.WriteErrorResponse(w, err)
   450  	}
   451  }
   452  
   453  // CheckFileHandler - check if a file metadata exists.
   454  func (s *storageRESTServer) CheckFileHandler(w http.ResponseWriter, r *http.Request) {
   455  	if !s.IsValid(w, r) {
   456  		return
   457  	}
   458  	vars := mux.Vars(r)
   459  	volume := vars[storageRESTVolume]
   460  	filePath := vars[storageRESTFilePath]
   461  
   462  	if err := s.storage.CheckFile(r.Context(), volume, filePath); err != nil {
   463  		s.WriteErrorResponse(w, err)
   464  	}
   465  }
   466  
   467  // ReadAllHandler - read all the contents of a file.
   468  func (s *storageRESTServer) ReadAllHandler(w http.ResponseWriter, r *http.Request) {
   469  	if !s.IsValid(w, r) {
   470  		return
   471  	}
   472  	vars := mux.Vars(r)
   473  	volume := vars[storageRESTVolume]
   474  	filePath := vars[storageRESTFilePath]
   475  
   476  	buf, err := s.storage.ReadAll(r.Context(), volume, filePath)
   477  	if err != nil {
   478  		s.WriteErrorResponse(w, err)
   479  		return
   480  	}
   481  	w.Header().Set(xhttp.ContentLength, strconv.Itoa(len(buf)))
   482  	w.Write(buf)
   483  	w.(http.Flusher).Flush()
   484  }
   485  
   486  // ReadFileHandler - read section of a file.
   487  func (s *storageRESTServer) ReadFileHandler(w http.ResponseWriter, r *http.Request) {
   488  	if !s.IsValid(w, r) {
   489  		return
   490  	}
   491  	vars := mux.Vars(r)
   492  	volume := vars[storageRESTVolume]
   493  	filePath := vars[storageRESTFilePath]
   494  	offset, err := strconv.Atoi(vars[storageRESTOffset])
   495  	if err != nil {
   496  		s.WriteErrorResponse(w, err)
   497  		return
   498  	}
   499  	length, err := strconv.Atoi(vars[storageRESTLength])
   500  	if err != nil {
   501  		s.WriteErrorResponse(w, err)
   502  		return
   503  	}
   504  	if offset < 0 || length < 0 {
   505  		s.WriteErrorResponse(w, errInvalidArgument)
   506  		return
   507  	}
   508  	var verifier *BitrotVerifier
   509  	if vars[storageRESTBitrotAlgo] != "" {
   510  		hashStr := vars[storageRESTBitrotHash]
   511  		var hash []byte
   512  		hash, err = hex.DecodeString(hashStr)
   513  		if err != nil {
   514  			s.WriteErrorResponse(w, err)
   515  			return
   516  		}
   517  		verifier = NewBitrotVerifier(BitrotAlgorithmFromString(vars[storageRESTBitrotAlgo]), hash)
   518  	}
   519  	buf := make([]byte, length)
   520  	_, err = s.storage.ReadFile(r.Context(), volume, filePath, int64(offset), buf, verifier)
   521  	if err != nil {
   522  		s.WriteErrorResponse(w, err)
   523  		return
   524  	}
   525  	w.Header().Set(xhttp.ContentLength, strconv.Itoa(len(buf)))
   526  	w.Write(buf)
   527  	w.(http.Flusher).Flush()
   528  }
   529  
   530  // ReadFileHandler - read section of a file.
   531  func (s *storageRESTServer) ReadFileStreamHandler(w http.ResponseWriter, r *http.Request) {
   532  	if !s.IsValid(w, r) {
   533  		return
   534  	}
   535  	vars := mux.Vars(r)
   536  	volume := vars[storageRESTVolume]
   537  	filePath := vars[storageRESTFilePath]
   538  	offset, err := strconv.Atoi(vars[storageRESTOffset])
   539  	if err != nil {
   540  		s.WriteErrorResponse(w, err)
   541  		return
   542  	}
   543  	length, err := strconv.Atoi(vars[storageRESTLength])
   544  	if err != nil {
   545  		s.WriteErrorResponse(w, err)
   546  		return
   547  	}
   548  
   549  	rc, err := s.storage.ReadFileStream(r.Context(), volume, filePath, int64(offset), int64(length))
   550  	if err != nil {
   551  		s.WriteErrorResponse(w, err)
   552  		return
   553  	}
   554  	defer rc.Close()
   555  
   556  	w.Header().Set(xhttp.ContentLength, strconv.Itoa(length))
   557  	if _, err = io.Copy(w, rc); err != nil {
   558  		if !xnet.IsNetworkOrHostDown(err, true) { // do not need to log disconnected clients
   559  			logger.LogIf(r.Context(), err)
   560  		}
   561  		return
   562  	}
   563  	w.(http.Flusher).Flush()
   564  }
   565  
   566  // ListDirHandler - list a directory.
   567  func (s *storageRESTServer) ListDirHandler(w http.ResponseWriter, r *http.Request) {
   568  	if !s.IsValid(w, r) {
   569  		return
   570  	}
   571  	vars := mux.Vars(r)
   572  	volume := vars[storageRESTVolume]
   573  	dirPath := vars[storageRESTDirPath]
   574  	count, err := strconv.Atoi(vars[storageRESTCount])
   575  	if err != nil {
   576  		s.WriteErrorResponse(w, err)
   577  		return
   578  	}
   579  
   580  	entries, err := s.storage.ListDir(r.Context(), volume, dirPath, count)
   581  	if err != nil {
   582  		s.WriteErrorResponse(w, err)
   583  		return
   584  	}
   585  	gob.NewEncoder(w).Encode(&entries)
   586  	w.(http.Flusher).Flush()
   587  }
   588  
   589  // DeleteFileHandler - delete a file.
   590  func (s *storageRESTServer) DeleteFileHandler(w http.ResponseWriter, r *http.Request) {
   591  	if !s.IsValid(w, r) {
   592  		return
   593  	}
   594  	vars := mux.Vars(r)
   595  	volume := vars[storageRESTVolume]
   596  	filePath := vars[storageRESTFilePath]
   597  	recursive, err := strconv.ParseBool(vars[storageRESTRecursive])
   598  	if err != nil {
   599  		s.WriteErrorResponse(w, err)
   600  		return
   601  	}
   602  
   603  	err = s.storage.Delete(r.Context(), volume, filePath, recursive)
   604  	if err != nil {
   605  		s.WriteErrorResponse(w, err)
   606  	}
   607  }
   608  
   609  // DeleteVersionsErrsResp - collection of delete errors
   610  // for bulk version deletes
   611  type DeleteVersionsErrsResp struct {
   612  	Errs []error
   613  }
   614  
   615  // DeleteVersionsHandler - delete a set of a versions.
   616  func (s *storageRESTServer) DeleteVersionsHandler(w http.ResponseWriter, r *http.Request) {
   617  	if !s.IsValid(w, r) {
   618  		return
   619  	}
   620  
   621  	vars := r.URL.Query()
   622  	volume := vars.Get(storageRESTVolume)
   623  
   624  	totalVersions, err := strconv.Atoi(vars.Get(storageRESTTotalVersions))
   625  	if err != nil {
   626  		s.WriteErrorResponse(w, err)
   627  		return
   628  	}
   629  
   630  	versions := make([]FileInfo, totalVersions)
   631  	decoder := msgp.NewReader(r.Body)
   632  	for i := 0; i < totalVersions; i++ {
   633  		dst := &versions[i]
   634  		if err := dst.DecodeMsg(decoder); err != nil {
   635  			s.WriteErrorResponse(w, err)
   636  			return
   637  		}
   638  	}
   639  
   640  	dErrsResp := &DeleteVersionsErrsResp{Errs: make([]error, totalVersions)}
   641  
   642  	setEventStreamHeaders(w)
   643  	encoder := gob.NewEncoder(w)
   644  	done := keepHTTPResponseAlive(w)
   645  	errs := s.storage.DeleteVersions(r.Context(), volume, versions)
   646  	done(nil)
   647  	for idx := range versions {
   648  		if errs[idx] != nil {
   649  			dErrsResp.Errs[idx] = StorageErr(errs[idx].Error())
   650  		}
   651  	}
   652  	encoder.Encode(dErrsResp)
   653  	w.(http.Flusher).Flush()
   654  }
   655  
   656  // RenameDataHandler - renames a meta object and data dir to destination.
   657  func (s *storageRESTServer) RenameDataHandler(w http.ResponseWriter, r *http.Request) {
   658  	if !s.IsValid(w, r) {
   659  		return
   660  	}
   661  
   662  	vars := mux.Vars(r)
   663  	srcVolume := vars[storageRESTSrcVolume]
   664  	srcFilePath := vars[storageRESTSrcPath]
   665  	dstVolume := vars[storageRESTDstVolume]
   666  	dstFilePath := vars[storageRESTDstPath]
   667  
   668  	if r.ContentLength < 0 {
   669  		s.WriteErrorResponse(w, errInvalidArgument)
   670  		return
   671  	}
   672  
   673  	var fi FileInfo
   674  	if err := msgp.Decode(r.Body, &fi); err != nil {
   675  		s.WriteErrorResponse(w, err)
   676  		return
   677  	}
   678  
   679  	err := s.storage.RenameData(r.Context(), srcVolume, srcFilePath, fi, dstVolume, dstFilePath)
   680  	if err != nil {
   681  		s.WriteErrorResponse(w, err)
   682  	}
   683  }
   684  
   685  // RenameFileHandler - rename a file.
   686  func (s *storageRESTServer) RenameFileHandler(w http.ResponseWriter, r *http.Request) {
   687  	if !s.IsValid(w, r) {
   688  		return
   689  	}
   690  	vars := mux.Vars(r)
   691  	srcVolume := vars[storageRESTSrcVolume]
   692  	srcFilePath := vars[storageRESTSrcPath]
   693  	dstVolume := vars[storageRESTDstVolume]
   694  	dstFilePath := vars[storageRESTDstPath]
   695  	err := s.storage.RenameFile(r.Context(), srcVolume, srcFilePath, dstVolume, dstFilePath)
   696  	if err != nil {
   697  		s.WriteErrorResponse(w, err)
   698  	}
   699  }
   700  
   701  // keepHTTPResponseAlive can be used to avoid timeouts with long storage
   702  // operations, such as bitrot verification or data usage scanning.
   703  // Every 10 seconds a space character is sent.
   704  // The returned function should always be called to release resources.
   705  // An optional error can be sent which will be picked as text only error,
   706  // without its original type by the receiver.
   707  // waitForHTTPResponse should be used to the receiving side.
   708  func keepHTTPResponseAlive(w http.ResponseWriter) func(error) {
   709  	doneCh := make(chan error)
   710  	go func() {
   711  		defer close(doneCh)
   712  		ticker := time.NewTicker(time.Second * 10)
   713  		for {
   714  			select {
   715  			case <-ticker.C:
   716  				// Response not ready, write a filler byte.
   717  				w.Write([]byte{32})
   718  				w.(http.Flusher).Flush()
   719  			case err := <-doneCh:
   720  				if err != nil {
   721  					w.Write([]byte{1})
   722  					w.Write([]byte(err.Error()))
   723  				} else {
   724  					w.Write([]byte{0})
   725  				}
   726  				ticker.Stop()
   727  				return
   728  			}
   729  		}
   730  	}()
   731  	return func(err error) {
   732  		if doneCh == nil {
   733  			return
   734  		}
   735  		// Indicate we are ready to write.
   736  		doneCh <- err
   737  
   738  		// Wait for channel to be closed so we don't race on writes.
   739  		<-doneCh
   740  
   741  		// Clear so we can be called multiple times without crashing.
   742  		doneCh = nil
   743  	}
   744  }
   745  
   746  // waitForHTTPResponse will wait for responses where keepHTTPResponseAlive
   747  // has been used.
   748  // The returned reader contains the payload.
   749  func waitForHTTPResponse(respBody io.Reader) (io.Reader, error) {
   750  	reader := bufio.NewReader(respBody)
   751  	for {
   752  		b, err := reader.ReadByte()
   753  		if err != nil {
   754  			return nil, err
   755  		}
   756  		// Check if we have a response ready or a filler byte.
   757  		switch b {
   758  		case 0:
   759  			return reader, nil
   760  		case 1:
   761  			errorText, err := ioutil.ReadAll(reader)
   762  			if err != nil {
   763  				return nil, err
   764  			}
   765  			return nil, errors.New(string(errorText))
   766  		case 32:
   767  			continue
   768  		default:
   769  			return nil, fmt.Errorf("unexpected filler byte: %d", b)
   770  		}
   771  	}
   772  }
   773  
   774  // drainCloser can be used for wrapping an http response.
   775  // It will drain the body before closing.
   776  type drainCloser struct {
   777  	rc io.ReadCloser
   778  }
   779  
   780  // Read forwards the read operation.
   781  func (f drainCloser) Read(p []byte) (n int, err error) {
   782  	return f.rc.Read(p)
   783  }
   784  
   785  // Close drains the body and closes the upstream.
   786  func (f drainCloser) Close() error {
   787  	xhttp.DrainBody(f.rc)
   788  	return nil
   789  }
   790  
   791  // httpStreamResponse allows streaming a response, but still send an error.
   792  type httpStreamResponse struct {
   793  	done  chan error
   794  	block chan []byte
   795  	err   error
   796  }
   797  
   798  // Write part of the the streaming response.
   799  // Note that upstream errors are currently not forwarded, but may be in the future.
   800  func (h *httpStreamResponse) Write(b []byte) (int, error) {
   801  	if len(b) == 0 || h.err != nil {
   802  		// Ignore 0 length blocks
   803  		return 0, h.err
   804  	}
   805  	tmp := make([]byte, len(b))
   806  	copy(tmp, b)
   807  	h.block <- tmp
   808  	return len(b), h.err
   809  }
   810  
   811  // CloseWithError will close the stream and return the specified error.
   812  // This can be done several times, but only the first error will be sent.
   813  // After calling this the stream should not be written to.
   814  func (h *httpStreamResponse) CloseWithError(err error) {
   815  	if h.done == nil {
   816  		return
   817  	}
   818  	h.done <- err
   819  	h.err = err
   820  	// Indicates that the response is done.
   821  	<-h.done
   822  	h.done = nil
   823  }
   824  
   825  // streamHTTPResponse can be used to avoid timeouts with long storage
   826  // operations, such as bitrot verification or data usage scanning.
   827  // Every 10 seconds a space character is sent.
   828  // The returned function should always be called to release resources.
   829  // An optional error can be sent which will be picked as text only error,
   830  // without its original type by the receiver.
   831  // waitForHTTPStream should be used to the receiving side.
   832  func streamHTTPResponse(w http.ResponseWriter) *httpStreamResponse {
   833  	doneCh := make(chan error)
   834  	blockCh := make(chan []byte)
   835  	h := httpStreamResponse{done: doneCh, block: blockCh}
   836  	go func() {
   837  		ticker := time.NewTicker(time.Second * 10)
   838  		for {
   839  			select {
   840  			case <-ticker.C:
   841  				// Response not ready, write a filler byte.
   842  				w.Write([]byte{32})
   843  				w.(http.Flusher).Flush()
   844  			case err := <-doneCh:
   845  				ticker.Stop()
   846  				defer close(doneCh)
   847  				if err != nil {
   848  					w.Write([]byte{1})
   849  					w.Write([]byte(err.Error()))
   850  				} else {
   851  					w.Write([]byte{0})
   852  				}
   853  				return
   854  			case block := <-blockCh:
   855  				var tmp [5]byte
   856  				tmp[0] = 2
   857  				binary.LittleEndian.PutUint32(tmp[1:], uint32(len(block)))
   858  				w.Write(tmp[:])
   859  				w.Write(block)
   860  				w.(http.Flusher).Flush()
   861  			}
   862  		}
   863  	}()
   864  	return &h
   865  }
   866  
   867  // waitForHTTPStream will wait for responses where
   868  // streamHTTPResponse has been used.
   869  // The returned reader contains the payload and must be closed if no error is returned.
   870  func waitForHTTPStream(respBody io.ReadCloser, w io.Writer) error {
   871  	var tmp [1]byte
   872  	for {
   873  		_, err := io.ReadFull(respBody, tmp[:])
   874  		if err != nil {
   875  			return err
   876  		}
   877  		// Check if we have a response ready or a filler byte.
   878  		switch tmp[0] {
   879  		case 0:
   880  			// 0 is unbuffered, copy the rest.
   881  			_, err := io.Copy(w, respBody)
   882  			respBody.Close()
   883  			if err == io.EOF {
   884  				return nil
   885  			}
   886  			return err
   887  		case 1:
   888  			errorText, err := ioutil.ReadAll(respBody)
   889  			if err != nil {
   890  				return err
   891  			}
   892  			respBody.Close()
   893  			return errors.New(string(errorText))
   894  		case 3:
   895  			// gob style is already deprecated, we can remove this when
   896  			// storage API version will be greater or equal to 23.
   897  			defer respBody.Close()
   898  			dec := gob.NewDecoder(respBody)
   899  			var err error
   900  			if de := dec.Decode(&err); de == nil {
   901  				return err
   902  			}
   903  			return errors.New("rpc error")
   904  		case 2:
   905  			// Block of data
   906  			var tmp [4]byte
   907  			_, err := io.ReadFull(respBody, tmp[:])
   908  			if err != nil {
   909  				return err
   910  			}
   911  			length := binary.LittleEndian.Uint32(tmp[:])
   912  			_, err = io.CopyN(w, respBody, int64(length))
   913  			if err != nil {
   914  				return err
   915  			}
   916  			continue
   917  		case 32:
   918  			continue
   919  		default:
   920  			go xhttp.DrainBody(respBody)
   921  			return fmt.Errorf("unexpected filler byte: %d", tmp[0])
   922  		}
   923  	}
   924  }
   925  
   926  // VerifyFileResp - VerifyFile()'s response.
   927  type VerifyFileResp struct {
   928  	Err error
   929  }
   930  
   931  // VerifyFileHandler - Verify all part of file for bitrot errors.
   932  func (s *storageRESTServer) VerifyFileHandler(w http.ResponseWriter, r *http.Request) {
   933  	if !s.IsValid(w, r) {
   934  		return
   935  	}
   936  	vars := mux.Vars(r)
   937  	volume := vars[storageRESTVolume]
   938  	filePath := vars[storageRESTFilePath]
   939  
   940  	if r.ContentLength < 0 {
   941  		s.WriteErrorResponse(w, errInvalidArgument)
   942  		return
   943  	}
   944  
   945  	var fi FileInfo
   946  	if err := msgp.Decode(r.Body, &fi); err != nil {
   947  		s.WriteErrorResponse(w, err)
   948  		return
   949  	}
   950  
   951  	setEventStreamHeaders(w)
   952  	encoder := gob.NewEncoder(w)
   953  	done := keepHTTPResponseAlive(w)
   954  	err := s.storage.VerifyFile(r.Context(), volume, filePath, fi)
   955  	done(nil)
   956  	vresp := &VerifyFileResp{}
   957  	if err != nil {
   958  		vresp.Err = StorageErr(err.Error())
   959  	}
   960  	encoder.Encode(vresp)
   961  	w.(http.Flusher).Flush()
   962  }
   963  
   964  // A single function to write certain errors to be fatal
   965  // or informative based on the `exit` flag, please look
   966  // at each implementation of error for added hints.
   967  //
   968  // FIXME: This is an unusual function but serves its purpose for
   969  // now, need to revist the overall erroring structure here.
   970  // Do not like it :-(
   971  func logFatalErrs(err error, endpoint Endpoint, exit bool) {
   972  	if errors.Is(err, errMinDiskSize) {
   973  		logger.Fatal(config.ErrUnableToWriteInBackend(err).Hint(err.Error()), "Unable to initialize backend")
   974  	} else if errors.Is(err, errUnsupportedDisk) {
   975  		var hint string
   976  		if endpoint.URL != nil {
   977  			hint = fmt.Sprintf("Disk '%s' does not support O_DIRECT flags, MinIO erasure coding requires filesystems with O_DIRECT support", endpoint.Path)
   978  		} else {
   979  			hint = "Disks do not support O_DIRECT flags, MinIO erasure coding requires filesystems with O_DIRECT support"
   980  		}
   981  		logger.Fatal(config.ErrUnsupportedBackend(err).Hint(hint), "Unable to initialize backend")
   982  	} else if errors.Is(err, errDiskNotDir) {
   983  		var hint string
   984  		if endpoint.URL != nil {
   985  			hint = fmt.Sprintf("Disk '%s' is not a directory, MinIO erasure coding needs a directory", endpoint.Path)
   986  		} else {
   987  			hint = "Disks are not directories, MinIO erasure coding needs directories"
   988  		}
   989  		logger.Fatal(config.ErrUnableToWriteInBackend(err).Hint(hint), "Unable to initialize backend")
   990  	} else if errors.Is(err, errFileAccessDenied) {
   991  		// Show a descriptive error with a hint about how to fix it.
   992  		var username string
   993  		if u, err := user.Current(); err == nil {
   994  			username = u.Username
   995  		} else {
   996  			username = "<your-username>"
   997  		}
   998  		var hint string
   999  		if endpoint.URL != nil {
  1000  			hint = fmt.Sprintf("Run the following command to add write permissions: `sudo chown -R %s %s && sudo chmod u+rxw %s`",
  1001  				username, endpoint.Path, endpoint.Path)
  1002  		} else {
  1003  			hint = fmt.Sprintf("Run the following command to add write permissions: `sudo chown -R %s. <path> && sudo chmod u+rxw <path>`", username)
  1004  		}
  1005  		logger.Fatal(config.ErrUnableToWriteInBackend(err).Hint(hint), "Unable to initialize backend")
  1006  	} else if errors.Is(err, errFaultyDisk) {
  1007  		if !exit {
  1008  			logger.LogIf(GlobalContext, fmt.Errorf("disk is faulty at %s, please replace the drive - disk will be offline", endpoint))
  1009  		} else {
  1010  			logger.Fatal(err, "Unable to initialize backend")
  1011  		}
  1012  	} else if errors.Is(err, errDiskFull) {
  1013  		if !exit {
  1014  			logger.LogIf(GlobalContext, fmt.Errorf("disk is already full at %s, incoming I/O will fail - disk will be offline", endpoint))
  1015  		} else {
  1016  			logger.Fatal(err, "Unable to initialize backend")
  1017  		}
  1018  	} else {
  1019  		if !exit {
  1020  			logger.LogIf(GlobalContext, fmt.Errorf("disk returned an unexpected error at %s, please investigate - disk will be offline", endpoint))
  1021  		} else {
  1022  			logger.Fatal(err, "Unable to initialize backend")
  1023  		}
  1024  	}
  1025  }
  1026  
  1027  // registerStorageRPCRouter - register storage rpc router.
  1028  func registerStorageRESTHandlers(router *mux.Router, endpointServerPools EndpointServerPools) {
  1029  	for _, ep := range endpointServerPools {
  1030  		for _, endpoint := range ep.Endpoints {
  1031  			if !endpoint.IsLocal {
  1032  				continue
  1033  			}
  1034  			storage, err := newXLStorage(endpoint)
  1035  			if err != nil {
  1036  				// if supported errors don't fail, we proceed to
  1037  				// printing message and moving forward.
  1038  				logFatalErrs(err, endpoint, false)
  1039  			}
  1040  
  1041  			server := &storageRESTServer{storage: storage}
  1042  
  1043  			subrouter := router.PathPrefix(path.Join(storageRESTPrefix, endpoint.Path)).Subrouter()
  1044  
  1045  			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodHealth).HandlerFunc(HTTPTraceHdrs(server.HealthHandler))
  1046  			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodDiskInfo).HandlerFunc(HTTPTraceHdrs(server.DiskInfoHandler))
  1047  			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodNSScanner).HandlerFunc(HTTPTraceHdrs(server.NSScannerHandler))
  1048  			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodMakeVol).HandlerFunc(HTTPTraceHdrs(server.MakeVolHandler)).Queries(restQueries(storageRESTVolume)...)
  1049  			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodMakeVolBulk).HandlerFunc(HTTPTraceHdrs(server.MakeVolBulkHandler)).Queries(restQueries(storageRESTVolumes)...)
  1050  			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodStatVol).HandlerFunc(HTTPTraceHdrs(server.StatVolHandler)).Queries(restQueries(storageRESTVolume)...)
  1051  			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodDeleteVol).HandlerFunc(HTTPTraceHdrs(server.DeleteVolHandler)).Queries(restQueries(storageRESTVolume)...)
  1052  			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodListVols).HandlerFunc(HTTPTraceHdrs(server.ListVolsHandler))
  1053  
  1054  			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodAppendFile).HandlerFunc(HTTPTraceHdrs(server.AppendFileHandler)).
  1055  				Queries(restQueries(storageRESTVolume, storageRESTFilePath)...)
  1056  			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodWriteAll).HandlerFunc(HTTPTraceHdrs(server.WriteAllHandler)).
  1057  				Queries(restQueries(storageRESTVolume, storageRESTFilePath)...)
  1058  			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodWriteMetadata).HandlerFunc(HTTPTraceHdrs(server.WriteMetadataHandler)).
  1059  				Queries(restQueries(storageRESTVolume, storageRESTFilePath)...)
  1060  			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodUpdateMetadata).HandlerFunc(HTTPTraceHdrs(server.UpdateMetadataHandler)).
  1061  				Queries(restQueries(storageRESTVolume, storageRESTFilePath)...)
  1062  			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodDeleteVersion).HandlerFunc(HTTPTraceHdrs(server.DeleteVersionHandler)).
  1063  				Queries(restQueries(storageRESTVolume, storageRESTFilePath, storageRESTForceDelMarker)...)
  1064  			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodReadVersion).HandlerFunc(HTTPTraceHdrs(server.ReadVersionHandler)).
  1065  				Queries(restQueries(storageRESTVolume, storageRESTFilePath, storageRESTVersionID, storageRESTReadData)...)
  1066  			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodRenameData).HandlerFunc(HTTPTraceHdrs(server.RenameDataHandler)).
  1067  				Queries(restQueries(storageRESTSrcVolume, storageRESTSrcPath,
  1068  					storageRESTDstVolume, storageRESTDstPath)...)
  1069  			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodCreateFile).HandlerFunc(HTTPTraceHdrs(server.CreateFileHandler)).
  1070  				Queries(restQueries(storageRESTVolume, storageRESTFilePath, storageRESTLength)...)
  1071  
  1072  			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodCheckFile).HandlerFunc(HTTPTraceHdrs(server.CheckFileHandler)).
  1073  				Queries(restQueries(storageRESTVolume, storageRESTFilePath)...)
  1074  			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodCheckParts).HandlerFunc(HTTPTraceHdrs(server.CheckPartsHandler)).
  1075  				Queries(restQueries(storageRESTVolume, storageRESTFilePath)...)
  1076  
  1077  			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodReadAll).HandlerFunc(HTTPTraceHdrs(server.ReadAllHandler)).
  1078  				Queries(restQueries(storageRESTVolume, storageRESTFilePath)...)
  1079  			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodReadFile).HandlerFunc(HTTPTraceHdrs(server.ReadFileHandler)).
  1080  				Queries(restQueries(storageRESTVolume, storageRESTFilePath, storageRESTOffset, storageRESTLength, storageRESTBitrotAlgo, storageRESTBitrotHash)...)
  1081  			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodReadFileStream).HandlerFunc(HTTPTraceHdrs(server.ReadFileStreamHandler)).
  1082  				Queries(restQueries(storageRESTVolume, storageRESTFilePath, storageRESTOffset, storageRESTLength)...)
  1083  			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodListDir).HandlerFunc(HTTPTraceHdrs(server.ListDirHandler)).
  1084  				Queries(restQueries(storageRESTVolume, storageRESTDirPath, storageRESTCount)...)
  1085  
  1086  			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodDeleteVersions).HandlerFunc(HTTPTraceHdrs(server.DeleteVersionsHandler)).
  1087  				Queries(restQueries(storageRESTVolume, storageRESTTotalVersions)...)
  1088  			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodDeleteFile).HandlerFunc(HTTPTraceHdrs(server.DeleteFileHandler)).
  1089  				Queries(restQueries(storageRESTVolume, storageRESTFilePath, storageRESTRecursive)...)
  1090  
  1091  			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodRenameFile).HandlerFunc(HTTPTraceHdrs(server.RenameFileHandler)).
  1092  				Queries(restQueries(storageRESTSrcVolume, storageRESTSrcPath, storageRESTDstVolume, storageRESTDstPath)...)
  1093  			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodVerifyFile).HandlerFunc(HTTPTraceHdrs(server.VerifyFileHandler)).
  1094  				Queries(restQueries(storageRESTVolume, storageRESTFilePath)...)
  1095  			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodWalkDir).HandlerFunc(HTTPTraceHdrs(server.WalkDirHandler)).
  1096  				Queries(restQueries(storageRESTVolume, storageRESTDirPath, storageRESTRecursive)...)
  1097  		}
  1098  	}
  1099  }