| 
									
										
										
										
											2021-04-19 03:41:13 +08:00
										 |  |  | // Copyright (c) 2015-2021 MinIO, Inc.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This file is part of MinIO Object Storage stack
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This program is free software: you can redistribute it and/or modify
 | 
					
						
							|  |  |  | // it under the terms of the GNU Affero General Public License as published by
 | 
					
						
							|  |  |  | // the Free Software Foundation, either version 3 of the License, or
 | 
					
						
							|  |  |  | // (at your option) any later version.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This program is distributed in the hope that it will be useful
 | 
					
						
							|  |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					
						
							|  |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					
						
							|  |  |  | // GNU Affero General Public License for more details.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // You should have received a copy of the GNU Affero General Public License
 | 
					
						
							|  |  |  | // along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | package cmd | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2020-02-02 10:11:29 +08:00
										 |  |  | 	"bufio" | 
					
						
							| 
									
										
										
										
											2021-05-20 05:38:30 +08:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2020-10-29 00:18:35 +08:00
										 |  |  | 	"encoding/binary" | 
					
						
							| 
									
										
										
										
											2019-03-19 04:07:58 +08:00
										 |  |  | 	"encoding/gob" | 
					
						
							|  |  |  | 	"encoding/hex" | 
					
						
							| 
									
										
										
										
											2019-02-14 07:29:46 +08:00
										 |  |  | 	"errors" | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"io" | 
					
						
							| 
									
										
										
										
											2019-03-19 04:07:58 +08:00
										 |  |  | 	"net/http" | 
					
						
							| 
									
										
										
										
											2019-12-26 14:05:54 +08:00
										 |  |  | 	"os/user" | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	"path" | 
					
						
							| 
									
										
										
										
											2021-11-09 00:41:27 +08:00
										 |  |  | 	"runtime/debug" | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	"strconv" | 
					
						
							| 
									
										
										
										
											2019-12-24 08:31:03 +08:00
										 |  |  | 	"strings" | 
					
						
							| 
									
										
										
										
											2021-05-19 08:25:00 +08:00
										 |  |  | 	"sync" | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	"time" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-03 09:07:52 +08:00
										 |  |  | 	"github.com/tinylib/msgp/msgp" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-06 03:20:08 +08:00
										 |  |  | 	jwtreq "github.com/golang-jwt/jwt/v4/request" | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	"github.com/gorilla/mux" | 
					
						
							| 
									
										
										
										
											2022-04-07 23:10:40 +08:00
										 |  |  | 	"github.com/minio/madmin-go" | 
					
						
							| 
									
										
										
										
											2021-06-02 05:59:40 +08:00
										 |  |  | 	"github.com/minio/minio/internal/config" | 
					
						
							|  |  |  | 	xhttp "github.com/minio/minio/internal/http" | 
					
						
							| 
									
										
										
										
											2021-11-02 23:11:50 +08:00
										 |  |  | 	xioutil "github.com/minio/minio/internal/ioutil" | 
					
						
							| 
									
										
										
										
											2021-06-02 05:59:40 +08:00
										 |  |  | 	xjwt "github.com/minio/minio/internal/jwt" | 
					
						
							|  |  |  | 	"github.com/minio/minio/internal/logger" | 
					
						
							| 
									
										
										
										
											2021-06-15 05:54:37 +08:00
										 |  |  | 	xnet "github.com/minio/pkg/net" | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-05 07:10:08 +08:00
										 |  |  | var errDiskStale = errors.New("drive stale") | 
					
						
							| 
									
										
										
										
											2019-02-14 07:29:46 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | // To abstract a disk over network.
 | 
					
						
							|  |  |  | type storageRESTServer struct { | 
					
						
							| 
									
										
										
										
											2022-03-10 03:38:54 +08:00
										 |  |  | 	storage *xlStorageDiskIDCheck | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (s *storageRESTServer) writeErrorResponse(w http.ResponseWriter, err error) { | 
					
						
							| 
									
										
										
										
											2020-06-18 05:49:26 +08:00
										 |  |  | 	if errors.Is(err, errDiskStale) { | 
					
						
							|  |  |  | 		w.WriteHeader(http.StatusPreconditionFailed) | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		w.WriteHeader(http.StatusForbidden) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	w.Write([]byte(err.Error())) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-18 14:16:27 +08:00
										 |  |  | // DefaultSkewTime - skew time is 15 minutes between minio peers.
 | 
					
						
							|  |  |  | const DefaultSkewTime = 15 * time.Minute | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-14 07:29:46 +08:00
										 |  |  | // Authenticates storage client's requests and validates for skewed time.
 | 
					
						
							|  |  |  | func storageServerRequestValidate(r *http.Request) error { | 
					
						
							| 
									
										
										
										
											2020-01-31 10:59:22 +08:00
										 |  |  | 	token, err := jwtreq.AuthorizationHeaderExtractor.ExtractToken(r) | 
					
						
							| 
									
										
										
										
											2019-04-04 03:16:19 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2020-01-31 10:59:22 +08:00
										 |  |  | 		if err == jwtreq.ErrNoTokenInRequest { | 
					
						
							|  |  |  | 			return errNoAuthToken | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-02-14 07:29:46 +08:00
										 |  |  | 		return err | 
					
						
							| 
									
										
										
										
											2019-02-13 05:24:14 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-01-31 10:59:22 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	claims := xjwt.NewStandardClaims() | 
					
						
							|  |  |  | 	if err = xjwt.ParseWithStandardClaims(token, claims, []byte(globalActiveCred.SecretKey)); err != nil { | 
					
						
							|  |  |  | 		return errAuthentication | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	owner := claims.AccessKey == globalActiveCred.AccessKey || claims.Subject == globalActiveCred.AccessKey | 
					
						
							|  |  |  | 	if !owner { | 
					
						
							|  |  |  | 		return errAuthentication | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-03 07:15:12 +08:00
										 |  |  | 	if claims.Audience != r.URL.RawQuery { | 
					
						
							| 
									
										
										
										
											2020-01-31 10:59:22 +08:00
										 |  |  | 		return errAuthentication | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	requestTimeStr := r.Header.Get("X-Minio-Time") | 
					
						
							|  |  |  | 	requestTime, err := time.Parse(time.RFC3339, requestTimeStr) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2019-02-14 07:29:46 +08:00
										 |  |  | 		return err | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	utcNow := UTCNow() | 
					
						
							|  |  |  | 	delta := requestTime.Sub(utcNow) | 
					
						
							|  |  |  | 	if delta < 0 { | 
					
						
							| 
									
										
										
										
											2020-01-31 10:59:22 +08:00
										 |  |  | 		delta *= -1 | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	if delta > DefaultSkewTime { | 
					
						
							| 
									
										
										
										
											2019-02-14 07:29:46 +08:00
										 |  |  | 		return fmt.Errorf("client time %v is too apart with server time %v", requestTime, utcNow) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-01-31 10:59:22 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-14 07:29:46 +08:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // IsValid - To authenticate and verify the time difference.
 | 
					
						
							| 
									
										
										
										
											2022-03-02 07:06:47 +08:00
										 |  |  | func (s *storageRESTServer) IsAuthValid(w http.ResponseWriter, r *http.Request) bool { | 
					
						
							| 
									
										
										
										
											2020-09-24 03:00:29 +08:00
										 |  |  | 	if s.storage == nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, errDiskNotFound) | 
					
						
							|  |  |  | 		return false | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-14 07:29:46 +08:00
										 |  |  | 	if err := storageServerRequestValidate(r); err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return false | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-09-17 12:14:35 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-02 07:06:47 +08:00
										 |  |  | 	return true | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // IsValid - To authenticate and check if the disk-id in the request corresponds to the underlying disk.
 | 
					
						
							|  |  |  | func (s *storageRESTServer) IsValid(w http.ResponseWriter, r *http.Request) bool { | 
					
						
							|  |  |  | 	if !s.IsAuthValid(w, r) { | 
					
						
							|  |  |  | 		return false | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-10 00:38:46 +08:00
										 |  |  | 	if err := r.ParseForm(); err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return false | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-08 13:43:01 +08:00
										 |  |  | 	diskID := r.Form.Get(storageRESTDiskID) | 
					
						
							| 
									
										
										
										
											2019-10-26 01:37:53 +08:00
										 |  |  | 	if diskID == "" { | 
					
						
							|  |  |  | 		// Request sent empty disk-id, we allow the request
 | 
					
						
							|  |  |  | 		// as the peer might be coming up and trying to read format.json
 | 
					
						
							|  |  |  | 		// or create format.json
 | 
					
						
							|  |  |  | 		return true | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-09-17 12:14:35 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-28 05:48:30 +08:00
										 |  |  | 	storedDiskID, err := s.storage.GetDiskID() | 
					
						
							| 
									
										
										
										
											2020-07-22 04:54:06 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return false | 
					
						
							| 
									
										
										
										
											2019-02-14 07:29:46 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-07-22 04:54:06 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if diskID != storedDiskID { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, errDiskStale) | 
					
						
							|  |  |  | 		return false | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// If format.json is available and request sent the right disk-id, we allow the request
 | 
					
						
							|  |  |  | 	return true | 
					
						
							| 
									
										
										
										
											2019-02-14 07:29:46 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-18 05:49:26 +08:00
										 |  |  | // HealthHandler handler checks if disk is stale
 | 
					
						
							|  |  |  | func (s *storageRESTServer) HealthHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	s.IsValid(w, r) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | // DiskInfoHandler - returns disk info.
 | 
					
						
							|  |  |  | func (s *storageRESTServer) DiskInfoHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							| 
									
										
										
										
											2022-03-02 07:06:47 +08:00
										 |  |  | 	if !s.IsAuthValid(w, r) { | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-09-05 00:45:06 +08:00
										 |  |  | 	info, err := s.storage.DiskInfo(r.Context()) | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2020-07-14 00:51:07 +08:00
										 |  |  | 		info.Error = err.Error() | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-11-05 02:10:54 +08:00
										 |  |  | 	logger.LogIf(r.Context(), msgp.Encode(w, &info)) | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-27 07:11:42 +08:00
										 |  |  | func (s *storageRESTServer) NSScannerHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							| 
									
										
										
										
											2019-12-12 22:02:37 +08:00
										 |  |  | 	if !s.IsValid(w, r) { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-07 23:10:40 +08:00
										 |  |  | 	scanMode, err := strconv.Atoi(r.Form.Get(storageRESTScanMode)) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		logger.LogIf(r.Context(), err) | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-31 10:45:12 +08:00
										 |  |  | 	setEventStreamHeaders(w) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-19 07:19:29 +08:00
										 |  |  | 	var cache dataUsageCache | 
					
						
							| 
									
										
										
										
											2022-04-07 23:10:40 +08:00
										 |  |  | 	err = cache.deserialize(r.Body) | 
					
						
							| 
									
										
										
										
											2019-12-12 22:02:37 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2020-03-19 07:19:29 +08:00
										 |  |  | 		logger.LogIf(r.Context(), err) | 
					
						
							| 
									
										
										
										
											2019-12-12 22:02:37 +08:00
										 |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-20 05:38:30 +08:00
										 |  |  | 	ctx, cancel := context.WithCancel(r.Context()) | 
					
						
							|  |  |  | 	defer cancel() | 
					
						
							| 
									
										
										
										
											2020-12-11 05:03:22 +08:00
										 |  |  | 	resp := streamHTTPResponse(w) | 
					
						
							| 
									
										
										
										
											2021-11-09 00:41:27 +08:00
										 |  |  | 	defer func() { | 
					
						
							|  |  |  | 		if r := recover(); r != nil { | 
					
						
							|  |  |  | 			debug.PrintStack() | 
					
						
							|  |  |  | 			resp.CloseWithError(fmt.Errorf("panic: %v", r)) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							| 
									
										
										
										
											2021-05-20 05:38:30 +08:00
										 |  |  | 	respW := msgp.NewWriter(resp) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Collect updates, stream them before the full cache is sent.
 | 
					
						
							|  |  |  | 	updates := make(chan dataUsageEntry, 1) | 
					
						
							|  |  |  | 	var wg sync.WaitGroup | 
					
						
							|  |  |  | 	wg.Add(1) | 
					
						
							|  |  |  | 	go func() { | 
					
						
							|  |  |  | 		defer wg.Done() | 
					
						
							|  |  |  | 		for update := range updates { | 
					
						
							|  |  |  | 			// Write true bool to indicate update.
 | 
					
						
							| 
									
										
										
										
											2021-07-03 02:19:56 +08:00
										 |  |  | 			var err error | 
					
						
							| 
									
										
										
										
											2021-05-20 05:38:30 +08:00
										 |  |  | 			if err = respW.WriteBool(true); err == nil { | 
					
						
							|  |  |  | 				err = update.EncodeMsg(respW) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			respW.Flush() | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				cancel() | 
					
						
							|  |  |  | 				resp.CloseWithError(err) | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							| 
									
										
										
										
											2022-04-07 23:10:40 +08:00
										 |  |  | 	usageInfo, err := s.storage.NSScanner(ctx, cache, updates, madmin.HealScanMode(scanMode)) | 
					
						
							| 
									
										
										
										
											2020-03-19 07:19:29 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-05-20 05:38:30 +08:00
										 |  |  | 		respW.Flush() | 
					
						
							| 
									
										
										
										
											2020-12-11 05:03:22 +08:00
										 |  |  | 		resp.CloseWithError(err) | 
					
						
							| 
									
										
										
										
											2020-03-19 07:19:29 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-05-20 05:38:30 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Write false bool to indicate we finished.
 | 
					
						
							|  |  |  | 	wg.Wait() | 
					
						
							|  |  |  | 	if err = respW.WriteBool(false); err == nil { | 
					
						
							|  |  |  | 		err = usageInfo.EncodeMsg(respW) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-07-03 02:19:56 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		resp.CloseWithError(err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-05-20 05:38:30 +08:00
										 |  |  | 	resp.CloseWithError(respW.Flush()) | 
					
						
							| 
									
										
										
										
											2019-12-12 22:02:37 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | // MakeVolHandler - make a volume.
 | 
					
						
							|  |  |  | func (s *storageRESTServer) MakeVolHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	if !s.IsValid(w, r) { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-12-10 00:38:46 +08:00
										 |  |  | 	volume := r.Form.Get(storageRESTVolume) | 
					
						
							| 
									
										
										
										
											2020-09-05 00:45:06 +08:00
										 |  |  | 	err := s.storage.MakeVol(r.Context(), volume) | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-24 08:31:03 +08:00
										 |  |  | // MakeVolBulkHandler - create multiple volumes as a bulk operation.
 | 
					
						
							|  |  |  | func (s *storageRESTServer) MakeVolBulkHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	if !s.IsValid(w, r) { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-12-10 00:38:46 +08:00
										 |  |  | 	volumes := strings.Split(r.Form.Get(storageRESTVolumes), ",") | 
					
						
							| 
									
										
										
										
											2020-09-05 00:45:06 +08:00
										 |  |  | 	err := s.storage.MakeVolBulk(r.Context(), volumes...) | 
					
						
							| 
									
										
										
										
											2019-12-24 08:31:03 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | // ListVolsHandler - list volumes.
 | 
					
						
							|  |  |  | func (s *storageRESTServer) ListVolsHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	if !s.IsValid(w, r) { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-09-05 00:45:06 +08:00
										 |  |  | 	infos, err := s.storage.ListVols(r.Context()) | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-11-05 02:10:54 +08:00
										 |  |  | 	logger.LogIf(r.Context(), msgp.Encode(w, VolsInfo(infos))) | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // StatVolHandler - stat a volume.
 | 
					
						
							|  |  |  | func (s *storageRESTServer) StatVolHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	if !s.IsValid(w, r) { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-12-10 00:38:46 +08:00
										 |  |  | 	volume := r.Form.Get(storageRESTVolume) | 
					
						
							| 
									
										
										
										
											2020-09-05 00:45:06 +08:00
										 |  |  | 	info, err := s.storage.StatVol(r.Context(), volume) | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-11-05 02:10:54 +08:00
										 |  |  | 	logger.LogIf(r.Context(), msgp.Encode(w, &info)) | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // DeleteVolumeHandler - delete a volume.
 | 
					
						
							|  |  |  | func (s *storageRESTServer) DeleteVolHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	if !s.IsValid(w, r) { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-12-10 00:38:46 +08:00
										 |  |  | 	volume := r.Form.Get(storageRESTVolume) | 
					
						
							| 
									
										
										
										
											2021-08-08 13:43:01 +08:00
										 |  |  | 	forceDelete := r.Form.Get(storageRESTForceDelete) == "true" | 
					
						
							| 
									
										
										
										
											2020-09-05 00:45:06 +08:00
										 |  |  | 	err := s.storage.DeleteVol(r.Context(), volume, forceDelete) | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | // AppendFileHandler - append data from the request to the file specified.
 | 
					
						
							|  |  |  | func (s *storageRESTServer) AppendFileHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	if !s.IsValid(w, r) { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-12-10 00:38:46 +08:00
										 |  |  | 	volume := r.Form.Get(storageRESTVolume) | 
					
						
							|  |  |  | 	filePath := r.Form.Get(storageRESTFilePath) | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	buf := make([]byte, r.ContentLength) | 
					
						
							|  |  |  | 	_, err := io.ReadFull(r.Body, buf) | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-09-05 00:45:06 +08:00
										 |  |  | 	err = s.storage.AppendFile(r.Context(), volume, filePath, buf) | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-19 03:41:13 +08:00
										 |  |  | // CreateFileHandler - copy the contents from the request.
 | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | func (s *storageRESTServer) CreateFileHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	if !s.IsValid(w, r) { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-12-10 00:38:46 +08:00
										 |  |  | 	volume := r.Form.Get(storageRESTVolume) | 
					
						
							|  |  |  | 	filePath := r.Form.Get(storageRESTFilePath) | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-10 00:38:46 +08:00
										 |  |  | 	fileSizeStr := r.Form.Get(storageRESTLength) | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | 	fileSize, err := strconv.Atoi(fileSizeStr) | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-03-25 00:05:03 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-28 00:16:36 +08:00
										 |  |  | 	done, body := keepHTTPReqResponseAlive(w, r) | 
					
						
							|  |  |  | 	done(s.storage.CreateFile(r.Context(), volume, filePath, int64(fileSize), body)) | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | // DeleteVersion delete updated metadata.
 | 
					
						
							|  |  |  | func (s *storageRESTServer) DeleteVersionHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	if !s.IsValid(w, r) { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-12-10 00:38:46 +08:00
										 |  |  | 	volume := r.Form.Get(storageRESTVolume) | 
					
						
							|  |  |  | 	filePath := r.Form.Get(storageRESTFilePath) | 
					
						
							|  |  |  | 	forceDelMarker, err := strconv.ParseBool(r.Form.Get(storageRESTForceDelMarker)) | 
					
						
							| 
									
										
										
										
											2021-02-04 02:33:43 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, errInvalidArgument) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-27 07:49:49 +08:00
										 |  |  | 	if r.ContentLength < 0 { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, errInvalidArgument) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-24 01:20:31 +08:00
										 |  |  | 	var fi FileInfo | 
					
						
							| 
									
										
										
										
											2020-11-03 09:07:52 +08:00
										 |  |  | 	if err := msgp.Decode(r.Body, &fi); err != nil { | 
					
						
							| 
									
										
										
										
											2020-06-24 01:20:31 +08:00
										 |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 02:33:43 +08:00
										 |  |  | 	err = s.storage.DeleteVersion(r.Context(), volume, filePath, fi, forceDelMarker) | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-27 07:49:49 +08:00
										 |  |  | // ReadVersion read metadata of versionID
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | func (s *storageRESTServer) ReadVersionHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	if !s.IsValid(w, r) { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-12-10 00:38:46 +08:00
										 |  |  | 	volume := r.Form.Get(storageRESTVolume) | 
					
						
							|  |  |  | 	filePath := r.Form.Get(storageRESTFilePath) | 
					
						
							|  |  |  | 	versionID := r.Form.Get(storageRESTVersionID) | 
					
						
							|  |  |  | 	readData, err := strconv.ParseBool(r.Form.Get(storageRESTReadData)) | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-01-11 18:27:04 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-08 11:27:31 +08:00
										 |  |  | 	fi, err := s.storage.ReadVersion(r.Context(), volume, filePath, versionID, readData) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-11 18:27:04 +08:00
										 |  |  | 	logger.LogIf(r.Context(), msgp.Encode(w, &fi)) | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // WriteMetadata write new updated metadata.
 | 
					
						
							|  |  |  | func (s *storageRESTServer) WriteMetadataHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	if !s.IsValid(w, r) { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-12-10 00:38:46 +08:00
										 |  |  | 	volume := r.Form.Get(storageRESTVolume) | 
					
						
							|  |  |  | 	filePath := r.Form.Get(storageRESTFilePath) | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if r.ContentLength < 0 { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, errInvalidArgument) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var fi FileInfo | 
					
						
							| 
									
										
										
										
											2020-11-03 09:07:52 +08:00
										 |  |  | 	if err := msgp.Decode(r.Body, &fi); err != nil { | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-03 09:07:52 +08:00
										 |  |  | 	err := s.storage.WriteMetadata(r.Context(), volume, filePath, fi) | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 04:32:31 +08:00
										 |  |  | // UpdateMetadata update new updated metadata.
 | 
					
						
							|  |  |  | func (s *storageRESTServer) UpdateMetadataHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	if !s.IsValid(w, r) { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-12-10 00:38:46 +08:00
										 |  |  | 	volume := r.Form.Get(storageRESTVolume) | 
					
						
							|  |  |  | 	filePath := r.Form.Get(storageRESTFilePath) | 
					
						
							| 
									
										
										
										
											2021-04-05 04:32:31 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if r.ContentLength < 0 { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, errInvalidArgument) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var fi FileInfo | 
					
						
							|  |  |  | 	if err := msgp.Decode(r.Body, &fi); err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	err := s.storage.UpdateMetadata(r.Context(), volume, filePath, fi) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-14 22:18:35 +08:00
										 |  |  | // WriteAllHandler - write to file all content.
 | 
					
						
							|  |  |  | func (s *storageRESTServer) WriteAllHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	if !s.IsValid(w, r) { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-12-10 00:38:46 +08:00
										 |  |  | 	volume := r.Form.Get(storageRESTVolume) | 
					
						
							|  |  |  | 	filePath := r.Form.Get(storageRESTFilePath) | 
					
						
							| 
									
										
										
										
											2018-11-14 22:18:35 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if r.ContentLength < 0 { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, errInvalidArgument) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-11-03 08:14:31 +08:00
										 |  |  | 	tmp := make([]byte, r.ContentLength) | 
					
						
							|  |  |  | 	_, err := io.ReadFull(r.Body, tmp) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	err = s.storage.WriteAll(r.Context(), volume, filePath, tmp) | 
					
						
							| 
									
										
										
										
											2018-11-14 22:18:35 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | // CheckPartsHandler - check if a file metadata exists.
 | 
					
						
							|  |  |  | func (s *storageRESTServer) CheckPartsHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	if !s.IsValid(w, r) { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-12-10 00:38:46 +08:00
										 |  |  | 	volume := r.Form.Get(storageRESTVolume) | 
					
						
							|  |  |  | 	filePath := r.Form.Get(storageRESTFilePath) | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	if r.ContentLength < 0 { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, errInvalidArgument) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var fi FileInfo | 
					
						
							| 
									
										
										
										
											2020-11-03 09:07:52 +08:00
										 |  |  | 	if err := msgp.Decode(r.Body, &fi); err != nil { | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-05 00:45:06 +08:00
										 |  |  | 	if err := s.storage.CheckParts(r.Context(), volume, filePath, fi); err != nil { | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | // ReadAllHandler - read all the contents of a file.
 | 
					
						
							|  |  |  | func (s *storageRESTServer) ReadAllHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	if !s.IsValid(w, r) { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-12-10 00:38:46 +08:00
										 |  |  | 	volume := r.Form.Get(storageRESTVolume) | 
					
						
							|  |  |  | 	filePath := r.Form.Get(storageRESTFilePath) | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-05 00:45:06 +08:00
										 |  |  | 	buf, err := s.storage.ReadAll(r.Context(), volume, filePath) | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-10-29 08:02:22 +08:00
										 |  |  | 	// Reuse after return.
 | 
					
						
							|  |  |  | 	defer metaDataPoolPut(buf) | 
					
						
							| 
									
										
										
										
											2019-07-03 13:34:32 +08:00
										 |  |  | 	w.Header().Set(xhttp.ContentLength, strconv.Itoa(len(buf))) | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	w.Write(buf) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-21 03:49:05 +08:00
										 |  |  | // ReadXLHandler - read xl.meta for an object at path.
 | 
					
						
							|  |  |  | func (s *storageRESTServer) ReadXLHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	if !s.IsValid(w, r) { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	volume := r.Form.Get(storageRESTVolume) | 
					
						
							|  |  |  | 	filePath := r.Form.Get(storageRESTFilePath) | 
					
						
							|  |  |  | 	readData, err := strconv.ParseBool(r.Form.Get(storageRESTReadData)) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	rf, err := s.storage.ReadXL(r.Context(), volume, filePath, readData) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	logger.LogIf(r.Context(), msgp.Encode(w, &rf)) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | // ReadFileHandler - read section of a file.
 | 
					
						
							|  |  |  | func (s *storageRESTServer) ReadFileHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	if !s.IsValid(w, r) { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-12-10 00:38:46 +08:00
										 |  |  | 	volume := r.Form.Get(storageRESTVolume) | 
					
						
							|  |  |  | 	filePath := r.Form.Get(storageRESTFilePath) | 
					
						
							|  |  |  | 	offset, err := strconv.Atoi(r.Form.Get(storageRESTOffset)) | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-12-10 00:38:46 +08:00
										 |  |  | 	length, err := strconv.Atoi(r.Form.Get(storageRESTLength)) | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if offset < 0 || length < 0 { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, errInvalidArgument) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	var verifier *BitrotVerifier | 
					
						
							| 
									
										
										
										
											2021-12-10 00:38:46 +08:00
										 |  |  | 	if r.Form.Get(storageRESTBitrotAlgo) != "" { | 
					
						
							|  |  |  | 		hashStr := r.Form.Get(storageRESTBitrotHash) | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 		var hash []byte | 
					
						
							|  |  |  | 		hash, err = hex.DecodeString(hashStr) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-12-10 00:38:46 +08:00
										 |  |  | 		verifier = NewBitrotVerifier(BitrotAlgorithmFromString(r.Form.Get(storageRESTBitrotAlgo)), hash) | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	buf := make([]byte, length) | 
					
						
							| 
									
										
										
										
											2021-10-29 08:02:22 +08:00
										 |  |  | 	defer metaDataPoolPut(buf) // Reuse if we can.
 | 
					
						
							| 
									
										
										
										
											2020-09-05 00:45:06 +08:00
										 |  |  | 	_, err = s.storage.ReadFile(r.Context(), volume, filePath, int64(offset), buf, verifier) | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-07-03 13:34:32 +08:00
										 |  |  | 	w.Header().Set(xhttp.ContentLength, strconv.Itoa(len(buf))) | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	w.Write(buf) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | // ReadFileHandler - read section of a file.
 | 
					
						
							|  |  |  | func (s *storageRESTServer) ReadFileStreamHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	if !s.IsValid(w, r) { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-12-10 00:38:46 +08:00
										 |  |  | 	volume := r.Form.Get(storageRESTVolume) | 
					
						
							|  |  |  | 	filePath := r.Form.Get(storageRESTFilePath) | 
					
						
							|  |  |  | 	offset, err := strconv.Atoi(r.Form.Get(storageRESTOffset)) | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-12-10 00:38:46 +08:00
										 |  |  | 	length, err := strconv.Atoi(r.Form.Get(storageRESTLength)) | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-03-18 13:20:26 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-05 00:45:06 +08:00
										 |  |  | 	rc, err := s.storage.ReadFileStream(r.Context(), volume, filePath, int64(offset), int64(length)) | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	defer rc.Close() | 
					
						
							| 
									
										
										
										
											2019-09-26 14:08:24 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-03 13:34:32 +08:00
										 |  |  | 	w.Header().Set(xhttp.ContentLength, strconv.Itoa(length)) | 
					
						
							| 
									
										
										
										
											2021-11-02 23:11:50 +08:00
										 |  |  | 	if _, err = xioutil.Copy(w, rc); err != nil { | 
					
						
							| 
									
										
										
										
											2021-03-01 07:33:03 +08:00
										 |  |  | 		if !xnet.IsNetworkOrHostDown(err, true) { // do not need to log disconnected clients
 | 
					
						
							|  |  |  | 			logger.LogIf(r.Context(), err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | // ListDirHandler - list a directory.
 | 
					
						
							|  |  |  | func (s *storageRESTServer) ListDirHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	if !s.IsValid(w, r) { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-12-10 00:38:46 +08:00
										 |  |  | 	volume := r.Form.Get(storageRESTVolume) | 
					
						
							|  |  |  | 	dirPath := r.Form.Get(storageRESTDirPath) | 
					
						
							|  |  |  | 	count, err := strconv.Atoi(r.Form.Get(storageRESTCount)) | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-03-22 13:10:13 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-05 00:45:06 +08:00
										 |  |  | 	entries, err := s.storage.ListDir(r.Context(), volume, dirPath, count) | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	gob.NewEncoder(w).Encode(&entries) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // DeleteFileHandler - delete a file.
 | 
					
						
							|  |  |  | func (s *storageRESTServer) DeleteFileHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	if !s.IsValid(w, r) { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-12-10 00:38:46 +08:00
										 |  |  | 	volume := r.Form.Get(storageRESTVolume) | 
					
						
							|  |  |  | 	filePath := r.Form.Get(storageRESTFilePath) | 
					
						
							|  |  |  | 	recursive, err := strconv.ParseBool(r.Form.Get(storageRESTRecursive)) | 
					
						
							| 
									
										
										
										
											2020-10-29 00:18:35 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-07-12 00:15:54 +08:00
										 |  |  | 	force, err := strconv.ParseBool(r.Form.Get(storageRESTForceDelete)) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	err = s.storage.Delete(r.Context(), volume, filePath, DeleteOptions{ | 
					
						
							|  |  |  | 		Recursive: recursive, | 
					
						
							|  |  |  | 		Force:     force, | 
					
						
							|  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | // DeleteVersionsErrsResp - collection of delete errors
 | 
					
						
							|  |  |  | // for bulk version deletes
 | 
					
						
							|  |  |  | type DeleteVersionsErrsResp struct { | 
					
						
							| 
									
										
										
										
											2019-10-01 10:01:28 +08:00
										 |  |  | 	Errs []error | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | // DeleteVersionsHandler - delete a set of a versions.
 | 
					
						
							|  |  |  | func (s *storageRESTServer) DeleteVersionsHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							| 
									
										
										
										
											2019-05-14 03:25:49 +08:00
										 |  |  | 	if !s.IsValid(w, r) { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-08 13:43:01 +08:00
										 |  |  | 	volume := r.Form.Get(storageRESTVolume) | 
					
						
							|  |  |  | 	totalVersions, err := strconv.Atoi(r.Form.Get(storageRESTTotalVersions)) | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2020-02-02 10:11:29 +08:00
										 |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-05-14 03:25:49 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-02 01:50:07 +08:00
										 |  |  | 	versions := make([]FileInfoVersions, totalVersions) | 
					
						
							| 
									
										
										
										
											2020-11-03 09:07:52 +08:00
										 |  |  | 	decoder := msgp.NewReader(r.Body) | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	for i := 0; i < totalVersions; i++ { | 
					
						
							| 
									
										
										
										
											2020-11-03 09:07:52 +08:00
										 |  |  | 		dst := &versions[i] | 
					
						
							|  |  |  | 		if err := dst.DecodeMsg(decoder); err != nil { | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 			s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	dErrsResp := &DeleteVersionsErrsResp{Errs: make([]error, totalVersions)} | 
					
						
							| 
									
										
										
										
											2020-03-22 13:10:13 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-31 10:45:12 +08:00
										 |  |  | 	setEventStreamHeaders(w) | 
					
						
							| 
									
										
										
										
											2020-03-11 23:56:36 +08:00
										 |  |  | 	encoder := gob.NewEncoder(w) | 
					
						
							| 
									
										
										
										
											2020-03-19 07:19:29 +08:00
										 |  |  | 	done := keepHTTPResponseAlive(w) | 
					
						
							| 
									
										
										
										
											2020-09-05 00:45:06 +08:00
										 |  |  | 	errs := s.storage.DeleteVersions(r.Context(), volume, versions) | 
					
						
							| 
									
										
										
										
											2020-05-12 11:41:38 +08:00
										 |  |  | 	done(nil) | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	for idx := range versions { | 
					
						
							|  |  |  | 		if errs[idx] != nil { | 
					
						
							|  |  |  | 			dErrsResp.Errs[idx] = StorageErr(errs[idx].Error()) | 
					
						
							| 
									
										
										
										
											2019-10-01 10:01:28 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-03-11 23:56:36 +08:00
										 |  |  | 	encoder.Encode(dErrsResp) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-06 07:51:37 +08:00
										 |  |  | // RenameDataResp - RenameData()'s response.
 | 
					
						
							|  |  |  | type RenameDataResp struct { | 
					
						
							|  |  |  | 	Signature uint64 | 
					
						
							|  |  |  | 	Err       error | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | // RenameDataHandler - renames a meta object and data dir to destination.
 | 
					
						
							|  |  |  | func (s *storageRESTServer) RenameDataHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							| 
									
										
										
										
											2020-03-11 23:56:36 +08:00
										 |  |  | 	if !s.IsValid(w, r) { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-04-21 01:44:39 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-10 00:38:46 +08:00
										 |  |  | 	srcVolume := r.Form.Get(storageRESTSrcVolume) | 
					
						
							|  |  |  | 	srcFilePath := r.Form.Get(storageRESTSrcPath) | 
					
						
							|  |  |  | 	dstVolume := r.Form.Get(storageRESTDstVolume) | 
					
						
							|  |  |  | 	dstFilePath := r.Form.Get(storageRESTDstPath) | 
					
						
							| 
									
										
										
										
											2021-04-21 01:44:39 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if r.ContentLength < 0 { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, errInvalidArgument) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var fi FileInfo | 
					
						
							|  |  |  | 	if err := msgp.Decode(r.Body, &fi); err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-06 07:51:37 +08:00
										 |  |  | 	setEventStreamHeaders(w) | 
					
						
							|  |  |  | 	encoder := gob.NewEncoder(w) | 
					
						
							|  |  |  | 	done := keepHTTPResponseAlive(w) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	sign, err := s.storage.RenameData(r.Context(), srcVolume, srcFilePath, fi, dstVolume, dstFilePath) | 
					
						
							|  |  |  | 	done(nil) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	resp := &RenameDataResp{ | 
					
						
							|  |  |  | 		Signature: sign, | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2022-09-06 07:51:37 +08:00
										 |  |  | 		resp.Err = StorageErr(err.Error()) | 
					
						
							| 
									
										
										
										
											2020-03-11 23:56:36 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-09-06 07:51:37 +08:00
										 |  |  | 	encoder.Encode(resp) | 
					
						
							| 
									
										
										
										
											2019-05-14 03:25:49 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | // RenameFileHandler - rename a file.
 | 
					
						
							|  |  |  | func (s *storageRESTServer) RenameFileHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	if !s.IsValid(w, r) { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-12-10 00:38:46 +08:00
										 |  |  | 	srcVolume := r.Form.Get(storageRESTSrcVolume) | 
					
						
							|  |  |  | 	srcFilePath := r.Form.Get(storageRESTSrcPath) | 
					
						
							|  |  |  | 	dstVolume := r.Form.Get(storageRESTDstVolume) | 
					
						
							|  |  |  | 	dstFilePath := r.Form.Get(storageRESTDstPath) | 
					
						
							| 
									
										
										
										
											2020-09-05 00:45:06 +08:00
										 |  |  | 	err := s.storage.RenameFile(r.Context(), srcVolume, srcFilePath, dstVolume, dstFilePath) | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-28 00:16:36 +08:00
										 |  |  | // closeNotifier is itself a ReadCloser that will notify when either an error occurs or
 | 
					
						
							|  |  |  | // the Close() function is called.
 | 
					
						
							|  |  |  | type closeNotifier struct { | 
					
						
							|  |  |  | 	rc   io.ReadCloser | 
					
						
							|  |  |  | 	done chan struct{} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (c *closeNotifier) Read(p []byte) (n int, err error) { | 
					
						
							|  |  |  | 	n, err = c.rc.Read(p) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		if c.done != nil { | 
					
						
							|  |  |  | 			close(c.done) | 
					
						
							|  |  |  | 			c.done = nil | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return n, err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (c *closeNotifier) Close() error { | 
					
						
							|  |  |  | 	if c.done != nil { | 
					
						
							|  |  |  | 		close(c.done) | 
					
						
							|  |  |  | 		c.done = nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return c.rc.Close() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // keepHTTPReqResponseAlive can be used to avoid timeouts with long storage
 | 
					
						
							|  |  |  | // operations, such as bitrot verification or data usage scanning.
 | 
					
						
							|  |  |  | // Every 10 seconds a space character is sent.
 | 
					
						
							|  |  |  | // keepHTTPReqResponseAlive will wait for the returned body to be read before starting the ticker.
 | 
					
						
							|  |  |  | // The returned function should always be called to release resources.
 | 
					
						
							|  |  |  | // An optional error can be sent which will be picked as text only error,
 | 
					
						
							|  |  |  | // without its original type by the receiver.
 | 
					
						
							|  |  |  | // waitForHTTPResponse should be used to the receiving side.
 | 
					
						
							|  |  |  | func keepHTTPReqResponseAlive(w http.ResponseWriter, r *http.Request) (resp func(error), body io.ReadCloser) { | 
					
						
							|  |  |  | 	bodyDoneCh := make(chan struct{}) | 
					
						
							|  |  |  | 	doneCh := make(chan error) | 
					
						
							|  |  |  | 	ctx := r.Context() | 
					
						
							|  |  |  | 	go func() { | 
					
						
							| 
									
										
										
										
											2022-01-03 01:15:06 +08:00
										 |  |  | 		canWrite := true | 
					
						
							| 
									
										
											  
											
												Fix panic (not fatal) on connection drops (#13811)
Fix more regressions from #13597 with double closed channels.
```
panic: "POST /minio/storage/data/distxl-plain/s1/d2/v42/createfile?disk-id=c789f7e1-2b52-442a-b518-aa2dac03f3a1&file-path=f6161668-b939-4543-9873-91b9da4cdff6%2F5eafa986-a3bf-4b1c-8bc0-03a37de390a3%2Fpart.1&length=2621760&volume=.minio.sys%2Ftmp": send on closed channel
goroutine 1977 [running]:
runtime/debug.Stack()
        c:/go/src/runtime/debug/stack.go:24 +0x65
github.com/minio/minio/cmd.setCriticalErrorHandler.func1.1()
        d:/minio/minio/cmd/generic-handlers.go:468 +0x8e
panic({0x2928860, 0x4fb17e0})
        c:/go/src/runtime/panic.go:1038 +0x215
github.com/minio/minio/cmd.keepHTTPReqResponseAlive.func2({0x4fe4ea0, 0xc02737d8a0})
        d:/minio/minio/cmd/storage-rest-server.go:818 +0x48
github.com/minio/minio/cmd.(*storageRESTServer).CreateFileHandler(0xc0015a8510, {0x50073e0, 0xc0273ec460}, 0xc029b9a400)
        d:/minio/minio/cmd/storage-rest-server.go:334 +0x1d2
net/http.HandlerFunc.ServeHTTP(...)
        c:/go/src/net/http/server.go:2046
github.com/minio/minio/cmd.httpTraceHdrs.func1({0x50073e0, 0xc0273ec460}, 0x0)
        d:/minio/minio/cmd/handler-utils.go:372 +0x53
net/http.HandlerFunc.ServeHTTP(0x5007380, {0x50073e0, 0xc0273ec460}, 0x10)
        c:/go/src/net/http/server.go:2046 +0x2f
github.com/minio/minio/cmd.addCustomHeaders.func1({0x5007380, 0xc0273dcf00}, 0xc0273f7340)
```
Reverts but adds write checks.
											
										 
											2021-12-03 03:22:32 +08:00
										 |  |  | 		write := func(b []byte) { | 
					
						
							|  |  |  | 			if canWrite { | 
					
						
							|  |  |  | 				n, err := w.Write(b) | 
					
						
							|  |  |  | 				if err != nil || n != len(b) { | 
					
						
							|  |  |  | 					canWrite = false | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-08-28 00:16:36 +08:00
										 |  |  | 		// Wait for body to be read.
 | 
					
						
							|  |  |  | 		select { | 
					
						
							|  |  |  | 		case <-ctx.Done(): | 
					
						
							|  |  |  | 		case <-bodyDoneCh: | 
					
						
							| 
									
										
											  
											
												Fix panic (not fatal) on connection drops (#13811)
Fix more regressions from #13597 with double closed channels.
```
panic: "POST /minio/storage/data/distxl-plain/s1/d2/v42/createfile?disk-id=c789f7e1-2b52-442a-b518-aa2dac03f3a1&file-path=f6161668-b939-4543-9873-91b9da4cdff6%2F5eafa986-a3bf-4b1c-8bc0-03a37de390a3%2Fpart.1&length=2621760&volume=.minio.sys%2Ftmp": send on closed channel
goroutine 1977 [running]:
runtime/debug.Stack()
        c:/go/src/runtime/debug/stack.go:24 +0x65
github.com/minio/minio/cmd.setCriticalErrorHandler.func1.1()
        d:/minio/minio/cmd/generic-handlers.go:468 +0x8e
panic({0x2928860, 0x4fb17e0})
        c:/go/src/runtime/panic.go:1038 +0x215
github.com/minio/minio/cmd.keepHTTPReqResponseAlive.func2({0x4fe4ea0, 0xc02737d8a0})
        d:/minio/minio/cmd/storage-rest-server.go:818 +0x48
github.com/minio/minio/cmd.(*storageRESTServer).CreateFileHandler(0xc0015a8510, {0x50073e0, 0xc0273ec460}, 0xc029b9a400)
        d:/minio/minio/cmd/storage-rest-server.go:334 +0x1d2
net/http.HandlerFunc.ServeHTTP(...)
        c:/go/src/net/http/server.go:2046
github.com/minio/minio/cmd.httpTraceHdrs.func1({0x50073e0, 0xc0273ec460}, 0x0)
        d:/minio/minio/cmd/handler-utils.go:372 +0x53
net/http.HandlerFunc.ServeHTTP(0x5007380, {0x50073e0, 0xc0273ec460}, 0x10)
        c:/go/src/net/http/server.go:2046 +0x2f
github.com/minio/minio/cmd.addCustomHeaders.func1({0x5007380, 0xc0273dcf00}, 0xc0273f7340)
```
Reverts but adds write checks.
											
										 
											2021-12-03 03:22:32 +08:00
										 |  |  | 		case err := <-doneCh: | 
					
						
							| 
									
										
										
										
											2021-08-28 00:16:36 +08:00
										 |  |  | 			if err != nil { | 
					
						
							| 
									
										
											  
											
												Fix panic (not fatal) on connection drops (#13811)
Fix more regressions from #13597 with double closed channels.
```
panic: "POST /minio/storage/data/distxl-plain/s1/d2/v42/createfile?disk-id=c789f7e1-2b52-442a-b518-aa2dac03f3a1&file-path=f6161668-b939-4543-9873-91b9da4cdff6%2F5eafa986-a3bf-4b1c-8bc0-03a37de390a3%2Fpart.1&length=2621760&volume=.minio.sys%2Ftmp": send on closed channel
goroutine 1977 [running]:
runtime/debug.Stack()
        c:/go/src/runtime/debug/stack.go:24 +0x65
github.com/minio/minio/cmd.setCriticalErrorHandler.func1.1()
        d:/minio/minio/cmd/generic-handlers.go:468 +0x8e
panic({0x2928860, 0x4fb17e0})
        c:/go/src/runtime/panic.go:1038 +0x215
github.com/minio/minio/cmd.keepHTTPReqResponseAlive.func2({0x4fe4ea0, 0xc02737d8a0})
        d:/minio/minio/cmd/storage-rest-server.go:818 +0x48
github.com/minio/minio/cmd.(*storageRESTServer).CreateFileHandler(0xc0015a8510, {0x50073e0, 0xc0273ec460}, 0xc029b9a400)
        d:/minio/minio/cmd/storage-rest-server.go:334 +0x1d2
net/http.HandlerFunc.ServeHTTP(...)
        c:/go/src/net/http/server.go:2046
github.com/minio/minio/cmd.httpTraceHdrs.func1({0x50073e0, 0xc0273ec460}, 0x0)
        d:/minio/minio/cmd/handler-utils.go:372 +0x53
net/http.HandlerFunc.ServeHTTP(0x5007380, {0x50073e0, 0xc0273ec460}, 0x10)
        c:/go/src/net/http/server.go:2046 +0x2f
github.com/minio/minio/cmd.addCustomHeaders.func1({0x5007380, 0xc0273dcf00}, 0xc0273f7340)
```
Reverts but adds write checks.
											
										 
											2021-12-03 03:22:32 +08:00
										 |  |  | 				write([]byte{1}) | 
					
						
							|  |  |  | 				write([]byte(err.Error())) | 
					
						
							| 
									
										
										
										
											2021-08-28 00:16:36 +08:00
										 |  |  | 			} else { | 
					
						
							| 
									
										
											  
											
												Fix panic (not fatal) on connection drops (#13811)
Fix more regressions from #13597 with double closed channels.
```
panic: "POST /minio/storage/data/distxl-plain/s1/d2/v42/createfile?disk-id=c789f7e1-2b52-442a-b518-aa2dac03f3a1&file-path=f6161668-b939-4543-9873-91b9da4cdff6%2F5eafa986-a3bf-4b1c-8bc0-03a37de390a3%2Fpart.1&length=2621760&volume=.minio.sys%2Ftmp": send on closed channel
goroutine 1977 [running]:
runtime/debug.Stack()
        c:/go/src/runtime/debug/stack.go:24 +0x65
github.com/minio/minio/cmd.setCriticalErrorHandler.func1.1()
        d:/minio/minio/cmd/generic-handlers.go:468 +0x8e
panic({0x2928860, 0x4fb17e0})
        c:/go/src/runtime/panic.go:1038 +0x215
github.com/minio/minio/cmd.keepHTTPReqResponseAlive.func2({0x4fe4ea0, 0xc02737d8a0})
        d:/minio/minio/cmd/storage-rest-server.go:818 +0x48
github.com/minio/minio/cmd.(*storageRESTServer).CreateFileHandler(0xc0015a8510, {0x50073e0, 0xc0273ec460}, 0xc029b9a400)
        d:/minio/minio/cmd/storage-rest-server.go:334 +0x1d2
net/http.HandlerFunc.ServeHTTP(...)
        c:/go/src/net/http/server.go:2046
github.com/minio/minio/cmd.httpTraceHdrs.func1({0x50073e0, 0xc0273ec460}, 0x0)
        d:/minio/minio/cmd/handler-utils.go:372 +0x53
net/http.HandlerFunc.ServeHTTP(0x5007380, {0x50073e0, 0xc0273ec460}, 0x10)
        c:/go/src/net/http/server.go:2046 +0x2f
github.com/minio/minio/cmd.addCustomHeaders.func1({0x5007380, 0xc0273dcf00}, 0xc0273f7340)
```
Reverts but adds write checks.
											
										 
											2021-12-03 03:22:32 +08:00
										 |  |  | 				write([]byte{0}) | 
					
						
							| 
									
										
										
										
											2021-08-28 00:16:36 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
											  
											
												Fix panic (not fatal) on connection drops (#13811)
Fix more regressions from #13597 with double closed channels.
```
panic: "POST /minio/storage/data/distxl-plain/s1/d2/v42/createfile?disk-id=c789f7e1-2b52-442a-b518-aa2dac03f3a1&file-path=f6161668-b939-4543-9873-91b9da4cdff6%2F5eafa986-a3bf-4b1c-8bc0-03a37de390a3%2Fpart.1&length=2621760&volume=.minio.sys%2Ftmp": send on closed channel
goroutine 1977 [running]:
runtime/debug.Stack()
        c:/go/src/runtime/debug/stack.go:24 +0x65
github.com/minio/minio/cmd.setCriticalErrorHandler.func1.1()
        d:/minio/minio/cmd/generic-handlers.go:468 +0x8e
panic({0x2928860, 0x4fb17e0})
        c:/go/src/runtime/panic.go:1038 +0x215
github.com/minio/minio/cmd.keepHTTPReqResponseAlive.func2({0x4fe4ea0, 0xc02737d8a0})
        d:/minio/minio/cmd/storage-rest-server.go:818 +0x48
github.com/minio/minio/cmd.(*storageRESTServer).CreateFileHandler(0xc0015a8510, {0x50073e0, 0xc0273ec460}, 0xc029b9a400)
        d:/minio/minio/cmd/storage-rest-server.go:334 +0x1d2
net/http.HandlerFunc.ServeHTTP(...)
        c:/go/src/net/http/server.go:2046
github.com/minio/minio/cmd.httpTraceHdrs.func1({0x50073e0, 0xc0273ec460}, 0x0)
        d:/minio/minio/cmd/handler-utils.go:372 +0x53
net/http.HandlerFunc.ServeHTTP(0x5007380, {0x50073e0, 0xc0273ec460}, 0x10)
        c:/go/src/net/http/server.go:2046 +0x2f
github.com/minio/minio/cmd.addCustomHeaders.func1({0x5007380, 0xc0273dcf00}, 0xc0273f7340)
```
Reverts but adds write checks.
											
										 
											2021-12-03 03:22:32 +08:00
										 |  |  | 			close(doneCh) | 
					
						
							| 
									
										
										
										
											2021-08-28 00:16:36 +08:00
										 |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
											  
											
												Fix panic (not fatal) on connection drops (#13811)
Fix more regressions from #13597 with double closed channels.
```
panic: "POST /minio/storage/data/distxl-plain/s1/d2/v42/createfile?disk-id=c789f7e1-2b52-442a-b518-aa2dac03f3a1&file-path=f6161668-b939-4543-9873-91b9da4cdff6%2F5eafa986-a3bf-4b1c-8bc0-03a37de390a3%2Fpart.1&length=2621760&volume=.minio.sys%2Ftmp": send on closed channel
goroutine 1977 [running]:
runtime/debug.Stack()
        c:/go/src/runtime/debug/stack.go:24 +0x65
github.com/minio/minio/cmd.setCriticalErrorHandler.func1.1()
        d:/minio/minio/cmd/generic-handlers.go:468 +0x8e
panic({0x2928860, 0x4fb17e0})
        c:/go/src/runtime/panic.go:1038 +0x215
github.com/minio/minio/cmd.keepHTTPReqResponseAlive.func2({0x4fe4ea0, 0xc02737d8a0})
        d:/minio/minio/cmd/storage-rest-server.go:818 +0x48
github.com/minio/minio/cmd.(*storageRESTServer).CreateFileHandler(0xc0015a8510, {0x50073e0, 0xc0273ec460}, 0xc029b9a400)
        d:/minio/minio/cmd/storage-rest-server.go:334 +0x1d2
net/http.HandlerFunc.ServeHTTP(...)
        c:/go/src/net/http/server.go:2046
github.com/minio/minio/cmd.httpTraceHdrs.func1({0x50073e0, 0xc0273ec460}, 0x0)
        d:/minio/minio/cmd/handler-utils.go:372 +0x53
net/http.HandlerFunc.ServeHTTP(0x5007380, {0x50073e0, 0xc0273ec460}, 0x10)
        c:/go/src/net/http/server.go:2046 +0x2f
github.com/minio/minio/cmd.addCustomHeaders.func1({0x5007380, 0xc0273dcf00}, 0xc0273f7340)
```
Reverts but adds write checks.
											
										 
											2021-12-03 03:22:32 +08:00
										 |  |  | 		defer close(doneCh) | 
					
						
							| 
									
										
										
										
											2021-08-28 00:16:36 +08:00
										 |  |  | 		// Initiate ticker after body has been read.
 | 
					
						
							|  |  |  | 		ticker := time.NewTicker(time.Second * 10) | 
					
						
							|  |  |  | 		for { | 
					
						
							|  |  |  | 			select { | 
					
						
							|  |  |  | 			case <-ticker.C: | 
					
						
							|  |  |  | 				// Response not ready, write a filler byte.
 | 
					
						
							| 
									
										
											  
											
												Fix panic (not fatal) on connection drops (#13811)
Fix more regressions from #13597 with double closed channels.
```
panic: "POST /minio/storage/data/distxl-plain/s1/d2/v42/createfile?disk-id=c789f7e1-2b52-442a-b518-aa2dac03f3a1&file-path=f6161668-b939-4543-9873-91b9da4cdff6%2F5eafa986-a3bf-4b1c-8bc0-03a37de390a3%2Fpart.1&length=2621760&volume=.minio.sys%2Ftmp": send on closed channel
goroutine 1977 [running]:
runtime/debug.Stack()
        c:/go/src/runtime/debug/stack.go:24 +0x65
github.com/minio/minio/cmd.setCriticalErrorHandler.func1.1()
        d:/minio/minio/cmd/generic-handlers.go:468 +0x8e
panic({0x2928860, 0x4fb17e0})
        c:/go/src/runtime/panic.go:1038 +0x215
github.com/minio/minio/cmd.keepHTTPReqResponseAlive.func2({0x4fe4ea0, 0xc02737d8a0})
        d:/minio/minio/cmd/storage-rest-server.go:818 +0x48
github.com/minio/minio/cmd.(*storageRESTServer).CreateFileHandler(0xc0015a8510, {0x50073e0, 0xc0273ec460}, 0xc029b9a400)
        d:/minio/minio/cmd/storage-rest-server.go:334 +0x1d2
net/http.HandlerFunc.ServeHTTP(...)
        c:/go/src/net/http/server.go:2046
github.com/minio/minio/cmd.httpTraceHdrs.func1({0x50073e0, 0xc0273ec460}, 0x0)
        d:/minio/minio/cmd/handler-utils.go:372 +0x53
net/http.HandlerFunc.ServeHTTP(0x5007380, {0x50073e0, 0xc0273ec460}, 0x10)
        c:/go/src/net/http/server.go:2046 +0x2f
github.com/minio/minio/cmd.addCustomHeaders.func1({0x5007380, 0xc0273dcf00}, 0xc0273f7340)
```
Reverts but adds write checks.
											
										 
											2021-12-03 03:22:32 +08:00
										 |  |  | 				write([]byte{32}) | 
					
						
							|  |  |  | 				if canWrite { | 
					
						
							|  |  |  | 					w.(http.Flusher).Flush() | 
					
						
							| 
									
										
										
										
											2021-11-19 09:19:58 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
											  
											
												Fix panic (not fatal) on connection drops (#13811)
Fix more regressions from #13597 with double closed channels.
```
panic: "POST /minio/storage/data/distxl-plain/s1/d2/v42/createfile?disk-id=c789f7e1-2b52-442a-b518-aa2dac03f3a1&file-path=f6161668-b939-4543-9873-91b9da4cdff6%2F5eafa986-a3bf-4b1c-8bc0-03a37de390a3%2Fpart.1&length=2621760&volume=.minio.sys%2Ftmp": send on closed channel
goroutine 1977 [running]:
runtime/debug.Stack()
        c:/go/src/runtime/debug/stack.go:24 +0x65
github.com/minio/minio/cmd.setCriticalErrorHandler.func1.1()
        d:/minio/minio/cmd/generic-handlers.go:468 +0x8e
panic({0x2928860, 0x4fb17e0})
        c:/go/src/runtime/panic.go:1038 +0x215
github.com/minio/minio/cmd.keepHTTPReqResponseAlive.func2({0x4fe4ea0, 0xc02737d8a0})
        d:/minio/minio/cmd/storage-rest-server.go:818 +0x48
github.com/minio/minio/cmd.(*storageRESTServer).CreateFileHandler(0xc0015a8510, {0x50073e0, 0xc0273ec460}, 0xc029b9a400)
        d:/minio/minio/cmd/storage-rest-server.go:334 +0x1d2
net/http.HandlerFunc.ServeHTTP(...)
        c:/go/src/net/http/server.go:2046
github.com/minio/minio/cmd.httpTraceHdrs.func1({0x50073e0, 0xc0273ec460}, 0x0)
        d:/minio/minio/cmd/handler-utils.go:372 +0x53
net/http.HandlerFunc.ServeHTTP(0x5007380, {0x50073e0, 0xc0273ec460}, 0x10)
        c:/go/src/net/http/server.go:2046 +0x2f
github.com/minio/minio/cmd.addCustomHeaders.func1({0x5007380, 0xc0273dcf00}, 0xc0273f7340)
```
Reverts but adds write checks.
											
										 
											2021-12-03 03:22:32 +08:00
										 |  |  | 			case err := <-doneCh: | 
					
						
							| 
									
										
										
										
											2021-08-28 00:16:36 +08:00
										 |  |  | 				if err != nil { | 
					
						
							| 
									
										
											  
											
												Fix panic (not fatal) on connection drops (#13811)
Fix more regressions from #13597 with double closed channels.
```
panic: "POST /minio/storage/data/distxl-plain/s1/d2/v42/createfile?disk-id=c789f7e1-2b52-442a-b518-aa2dac03f3a1&file-path=f6161668-b939-4543-9873-91b9da4cdff6%2F5eafa986-a3bf-4b1c-8bc0-03a37de390a3%2Fpart.1&length=2621760&volume=.minio.sys%2Ftmp": send on closed channel
goroutine 1977 [running]:
runtime/debug.Stack()
        c:/go/src/runtime/debug/stack.go:24 +0x65
github.com/minio/minio/cmd.setCriticalErrorHandler.func1.1()
        d:/minio/minio/cmd/generic-handlers.go:468 +0x8e
panic({0x2928860, 0x4fb17e0})
        c:/go/src/runtime/panic.go:1038 +0x215
github.com/minio/minio/cmd.keepHTTPReqResponseAlive.func2({0x4fe4ea0, 0xc02737d8a0})
        d:/minio/minio/cmd/storage-rest-server.go:818 +0x48
github.com/minio/minio/cmd.(*storageRESTServer).CreateFileHandler(0xc0015a8510, {0x50073e0, 0xc0273ec460}, 0xc029b9a400)
        d:/minio/minio/cmd/storage-rest-server.go:334 +0x1d2
net/http.HandlerFunc.ServeHTTP(...)
        c:/go/src/net/http/server.go:2046
github.com/minio/minio/cmd.httpTraceHdrs.func1({0x50073e0, 0xc0273ec460}, 0x0)
        d:/minio/minio/cmd/handler-utils.go:372 +0x53
net/http.HandlerFunc.ServeHTTP(0x5007380, {0x50073e0, 0xc0273ec460}, 0x10)
        c:/go/src/net/http/server.go:2046 +0x2f
github.com/minio/minio/cmd.addCustomHeaders.func1({0x5007380, 0xc0273dcf00}, 0xc0273f7340)
```
Reverts but adds write checks.
											
										 
											2021-12-03 03:22:32 +08:00
										 |  |  | 					write([]byte{1}) | 
					
						
							|  |  |  | 					write([]byte(err.Error())) | 
					
						
							| 
									
										
										
										
											2021-08-28 00:16:36 +08:00
										 |  |  | 				} else { | 
					
						
							| 
									
										
											  
											
												Fix panic (not fatal) on connection drops (#13811)
Fix more regressions from #13597 with double closed channels.
```
panic: "POST /minio/storage/data/distxl-plain/s1/d2/v42/createfile?disk-id=c789f7e1-2b52-442a-b518-aa2dac03f3a1&file-path=f6161668-b939-4543-9873-91b9da4cdff6%2F5eafa986-a3bf-4b1c-8bc0-03a37de390a3%2Fpart.1&length=2621760&volume=.minio.sys%2Ftmp": send on closed channel
goroutine 1977 [running]:
runtime/debug.Stack()
        c:/go/src/runtime/debug/stack.go:24 +0x65
github.com/minio/minio/cmd.setCriticalErrorHandler.func1.1()
        d:/minio/minio/cmd/generic-handlers.go:468 +0x8e
panic({0x2928860, 0x4fb17e0})
        c:/go/src/runtime/panic.go:1038 +0x215
github.com/minio/minio/cmd.keepHTTPReqResponseAlive.func2({0x4fe4ea0, 0xc02737d8a0})
        d:/minio/minio/cmd/storage-rest-server.go:818 +0x48
github.com/minio/minio/cmd.(*storageRESTServer).CreateFileHandler(0xc0015a8510, {0x50073e0, 0xc0273ec460}, 0xc029b9a400)
        d:/minio/minio/cmd/storage-rest-server.go:334 +0x1d2
net/http.HandlerFunc.ServeHTTP(...)
        c:/go/src/net/http/server.go:2046
github.com/minio/minio/cmd.httpTraceHdrs.func1({0x50073e0, 0xc0273ec460}, 0x0)
        d:/minio/minio/cmd/handler-utils.go:372 +0x53
net/http.HandlerFunc.ServeHTTP(0x5007380, {0x50073e0, 0xc0273ec460}, 0x10)
        c:/go/src/net/http/server.go:2046 +0x2f
github.com/minio/minio/cmd.addCustomHeaders.func1({0x5007380, 0xc0273dcf00}, 0xc0273f7340)
```
Reverts but adds write checks.
											
										 
											2021-12-03 03:22:32 +08:00
										 |  |  | 					write([]byte{0}) | 
					
						
							| 
									
										
										
										
											2021-08-28 00:16:36 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
											  
											
												Fix panic (not fatal) on connection drops (#13811)
Fix more regressions from #13597 with double closed channels.
```
panic: "POST /minio/storage/data/distxl-plain/s1/d2/v42/createfile?disk-id=c789f7e1-2b52-442a-b518-aa2dac03f3a1&file-path=f6161668-b939-4543-9873-91b9da4cdff6%2F5eafa986-a3bf-4b1c-8bc0-03a37de390a3%2Fpart.1&length=2621760&volume=.minio.sys%2Ftmp": send on closed channel
goroutine 1977 [running]:
runtime/debug.Stack()
        c:/go/src/runtime/debug/stack.go:24 +0x65
github.com/minio/minio/cmd.setCriticalErrorHandler.func1.1()
        d:/minio/minio/cmd/generic-handlers.go:468 +0x8e
panic({0x2928860, 0x4fb17e0})
        c:/go/src/runtime/panic.go:1038 +0x215
github.com/minio/minio/cmd.keepHTTPReqResponseAlive.func2({0x4fe4ea0, 0xc02737d8a0})
        d:/minio/minio/cmd/storage-rest-server.go:818 +0x48
github.com/minio/minio/cmd.(*storageRESTServer).CreateFileHandler(0xc0015a8510, {0x50073e0, 0xc0273ec460}, 0xc029b9a400)
        d:/minio/minio/cmd/storage-rest-server.go:334 +0x1d2
net/http.HandlerFunc.ServeHTTP(...)
        c:/go/src/net/http/server.go:2046
github.com/minio/minio/cmd.httpTraceHdrs.func1({0x50073e0, 0xc0273ec460}, 0x0)
        d:/minio/minio/cmd/handler-utils.go:372 +0x53
net/http.HandlerFunc.ServeHTTP(0x5007380, {0x50073e0, 0xc0273ec460}, 0x10)
        c:/go/src/net/http/server.go:2046 +0x2f
github.com/minio/minio/cmd.addCustomHeaders.func1({0x5007380, 0xc0273dcf00}, 0xc0273f7340)
```
Reverts but adds write checks.
											
										 
											2021-12-03 03:22:32 +08:00
										 |  |  | 				ticker.Stop() | 
					
						
							| 
									
										
										
										
											2021-08-28 00:16:36 +08:00
										 |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 	return func(err error) { | 
					
						
							|  |  |  | 		if doneCh == nil { | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Indicate we are ready to write.
 | 
					
						
							|  |  |  | 		doneCh <- err | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Wait for channel to be closed so we don't race on writes.
 | 
					
						
							|  |  |  | 		<-doneCh | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Clear so we can be called multiple times without crashing.
 | 
					
						
							|  |  |  | 		doneCh = nil | 
					
						
							|  |  |  | 	}, &closeNotifier{rc: r.Body, done: bodyDoneCh} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-19 07:19:29 +08:00
										 |  |  | // keepHTTPResponseAlive can be used to avoid timeouts with long storage
 | 
					
						
							| 
									
										
										
										
											2021-02-27 07:11:42 +08:00
										 |  |  | // operations, such as bitrot verification or data usage scanning.
 | 
					
						
							| 
									
										
										
										
											2021-08-28 00:16:36 +08:00
										 |  |  | // keepHTTPResponseAlive may NOT be used until the request body has been read,
 | 
					
						
							|  |  |  | // use keepHTTPReqResponseAlive instead.
 | 
					
						
							| 
									
										
										
										
											2020-03-19 07:19:29 +08:00
										 |  |  | // Every 10 seconds a space character is sent.
 | 
					
						
							|  |  |  | // The returned function should always be called to release resources.
 | 
					
						
							| 
									
										
										
										
											2020-05-12 11:41:38 +08:00
										 |  |  | // An optional error can be sent which will be picked as text only error,
 | 
					
						
							|  |  |  | // without its original type by the receiver.
 | 
					
						
							| 
									
										
										
										
											2020-03-19 07:19:29 +08:00
										 |  |  | // waitForHTTPResponse should be used to the receiving side.
 | 
					
						
							| 
									
										
										
										
											2020-05-12 11:41:38 +08:00
										 |  |  | func keepHTTPResponseAlive(w http.ResponseWriter) func(error) { | 
					
						
							|  |  |  | 	doneCh := make(chan error) | 
					
						
							| 
									
										
										
										
											2019-07-09 04:51:18 +08:00
										 |  |  | 	go func() { | 
					
						
							| 
									
										
										
										
											2022-01-03 01:15:06 +08:00
										 |  |  | 		canWrite := true | 
					
						
							| 
									
										
											  
											
												Fix panic (not fatal) on connection drops (#13811)
Fix more regressions from #13597 with double closed channels.
```
panic: "POST /minio/storage/data/distxl-plain/s1/d2/v42/createfile?disk-id=c789f7e1-2b52-442a-b518-aa2dac03f3a1&file-path=f6161668-b939-4543-9873-91b9da4cdff6%2F5eafa986-a3bf-4b1c-8bc0-03a37de390a3%2Fpart.1&length=2621760&volume=.minio.sys%2Ftmp": send on closed channel
goroutine 1977 [running]:
runtime/debug.Stack()
        c:/go/src/runtime/debug/stack.go:24 +0x65
github.com/minio/minio/cmd.setCriticalErrorHandler.func1.1()
        d:/minio/minio/cmd/generic-handlers.go:468 +0x8e
panic({0x2928860, 0x4fb17e0})
        c:/go/src/runtime/panic.go:1038 +0x215
github.com/minio/minio/cmd.keepHTTPReqResponseAlive.func2({0x4fe4ea0, 0xc02737d8a0})
        d:/minio/minio/cmd/storage-rest-server.go:818 +0x48
github.com/minio/minio/cmd.(*storageRESTServer).CreateFileHandler(0xc0015a8510, {0x50073e0, 0xc0273ec460}, 0xc029b9a400)
        d:/minio/minio/cmd/storage-rest-server.go:334 +0x1d2
net/http.HandlerFunc.ServeHTTP(...)
        c:/go/src/net/http/server.go:2046
github.com/minio/minio/cmd.httpTraceHdrs.func1({0x50073e0, 0xc0273ec460}, 0x0)
        d:/minio/minio/cmd/handler-utils.go:372 +0x53
net/http.HandlerFunc.ServeHTTP(0x5007380, {0x50073e0, 0xc0273ec460}, 0x10)
        c:/go/src/net/http/server.go:2046 +0x2f
github.com/minio/minio/cmd.addCustomHeaders.func1({0x5007380, 0xc0273dcf00}, 0xc0273f7340)
```
Reverts but adds write checks.
											
										 
											2021-12-03 03:22:32 +08:00
										 |  |  | 		write := func(b []byte) { | 
					
						
							|  |  |  | 			if canWrite { | 
					
						
							|  |  |  | 				n, err := w.Write(b) | 
					
						
							|  |  |  | 				if err != nil || n != len(b) { | 
					
						
							|  |  |  | 					canWrite = false | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-03-19 07:19:29 +08:00
										 |  |  | 		defer close(doneCh) | 
					
						
							| 
									
										
										
										
											2019-07-09 04:51:18 +08:00
										 |  |  | 		ticker := time.NewTicker(time.Second * 10) | 
					
						
							| 
									
										
										
										
											2021-11-19 09:19:58 +08:00
										 |  |  | 		defer ticker.Stop() | 
					
						
							| 
									
										
										
										
											2019-07-09 04:51:18 +08:00
										 |  |  | 		for { | 
					
						
							|  |  |  | 			select { | 
					
						
							|  |  |  | 			case <-ticker.C: | 
					
						
							| 
									
										
										
										
											2020-05-12 11:41:38 +08:00
										 |  |  | 				// Response not ready, write a filler byte.
 | 
					
						
							| 
									
										
											  
											
												Fix panic (not fatal) on connection drops (#13811)
Fix more regressions from #13597 with double closed channels.
```
panic: "POST /minio/storage/data/distxl-plain/s1/d2/v42/createfile?disk-id=c789f7e1-2b52-442a-b518-aa2dac03f3a1&file-path=f6161668-b939-4543-9873-91b9da4cdff6%2F5eafa986-a3bf-4b1c-8bc0-03a37de390a3%2Fpart.1&length=2621760&volume=.minio.sys%2Ftmp": send on closed channel
goroutine 1977 [running]:
runtime/debug.Stack()
        c:/go/src/runtime/debug/stack.go:24 +0x65
github.com/minio/minio/cmd.setCriticalErrorHandler.func1.1()
        d:/minio/minio/cmd/generic-handlers.go:468 +0x8e
panic({0x2928860, 0x4fb17e0})
        c:/go/src/runtime/panic.go:1038 +0x215
github.com/minio/minio/cmd.keepHTTPReqResponseAlive.func2({0x4fe4ea0, 0xc02737d8a0})
        d:/minio/minio/cmd/storage-rest-server.go:818 +0x48
github.com/minio/minio/cmd.(*storageRESTServer).CreateFileHandler(0xc0015a8510, {0x50073e0, 0xc0273ec460}, 0xc029b9a400)
        d:/minio/minio/cmd/storage-rest-server.go:334 +0x1d2
net/http.HandlerFunc.ServeHTTP(...)
        c:/go/src/net/http/server.go:2046
github.com/minio/minio/cmd.httpTraceHdrs.func1({0x50073e0, 0xc0273ec460}, 0x0)
        d:/minio/minio/cmd/handler-utils.go:372 +0x53
net/http.HandlerFunc.ServeHTTP(0x5007380, {0x50073e0, 0xc0273ec460}, 0x10)
        c:/go/src/net/http/server.go:2046 +0x2f
github.com/minio/minio/cmd.addCustomHeaders.func1({0x5007380, 0xc0273dcf00}, 0xc0273f7340)
```
Reverts but adds write checks.
											
										 
											2021-12-03 03:22:32 +08:00
										 |  |  | 				write([]byte{32}) | 
					
						
							|  |  |  | 				if canWrite { | 
					
						
							|  |  |  | 					w.(http.Flusher).Flush() | 
					
						
							| 
									
										
										
										
											2021-11-19 09:19:58 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2020-05-12 11:41:38 +08:00
										 |  |  | 			case err := <-doneCh: | 
					
						
							|  |  |  | 				if err != nil { | 
					
						
							| 
									
										
											  
											
												Fix panic (not fatal) on connection drops (#13811)
Fix more regressions from #13597 with double closed channels.
```
panic: "POST /minio/storage/data/distxl-plain/s1/d2/v42/createfile?disk-id=c789f7e1-2b52-442a-b518-aa2dac03f3a1&file-path=f6161668-b939-4543-9873-91b9da4cdff6%2F5eafa986-a3bf-4b1c-8bc0-03a37de390a3%2Fpart.1&length=2621760&volume=.minio.sys%2Ftmp": send on closed channel
goroutine 1977 [running]:
runtime/debug.Stack()
        c:/go/src/runtime/debug/stack.go:24 +0x65
github.com/minio/minio/cmd.setCriticalErrorHandler.func1.1()
        d:/minio/minio/cmd/generic-handlers.go:468 +0x8e
panic({0x2928860, 0x4fb17e0})
        c:/go/src/runtime/panic.go:1038 +0x215
github.com/minio/minio/cmd.keepHTTPReqResponseAlive.func2({0x4fe4ea0, 0xc02737d8a0})
        d:/minio/minio/cmd/storage-rest-server.go:818 +0x48
github.com/minio/minio/cmd.(*storageRESTServer).CreateFileHandler(0xc0015a8510, {0x50073e0, 0xc0273ec460}, 0xc029b9a400)
        d:/minio/minio/cmd/storage-rest-server.go:334 +0x1d2
net/http.HandlerFunc.ServeHTTP(...)
        c:/go/src/net/http/server.go:2046
github.com/minio/minio/cmd.httpTraceHdrs.func1({0x50073e0, 0xc0273ec460}, 0x0)
        d:/minio/minio/cmd/handler-utils.go:372 +0x53
net/http.HandlerFunc.ServeHTTP(0x5007380, {0x50073e0, 0xc0273ec460}, 0x10)
        c:/go/src/net/http/server.go:2046 +0x2f
github.com/minio/minio/cmd.addCustomHeaders.func1({0x5007380, 0xc0273dcf00}, 0xc0273f7340)
```
Reverts but adds write checks.
											
										 
											2021-12-03 03:22:32 +08:00
										 |  |  | 					write([]byte{1}) | 
					
						
							|  |  |  | 					write([]byte(err.Error())) | 
					
						
							| 
									
										
										
										
											2020-05-12 11:41:38 +08:00
										 |  |  | 				} else { | 
					
						
							| 
									
										
											  
											
												Fix panic (not fatal) on connection drops (#13811)
Fix more regressions from #13597 with double closed channels.
```
panic: "POST /minio/storage/data/distxl-plain/s1/d2/v42/createfile?disk-id=c789f7e1-2b52-442a-b518-aa2dac03f3a1&file-path=f6161668-b939-4543-9873-91b9da4cdff6%2F5eafa986-a3bf-4b1c-8bc0-03a37de390a3%2Fpart.1&length=2621760&volume=.minio.sys%2Ftmp": send on closed channel
goroutine 1977 [running]:
runtime/debug.Stack()
        c:/go/src/runtime/debug/stack.go:24 +0x65
github.com/minio/minio/cmd.setCriticalErrorHandler.func1.1()
        d:/minio/minio/cmd/generic-handlers.go:468 +0x8e
panic({0x2928860, 0x4fb17e0})
        c:/go/src/runtime/panic.go:1038 +0x215
github.com/minio/minio/cmd.keepHTTPReqResponseAlive.func2({0x4fe4ea0, 0xc02737d8a0})
        d:/minio/minio/cmd/storage-rest-server.go:818 +0x48
github.com/minio/minio/cmd.(*storageRESTServer).CreateFileHandler(0xc0015a8510, {0x50073e0, 0xc0273ec460}, 0xc029b9a400)
        d:/minio/minio/cmd/storage-rest-server.go:334 +0x1d2
net/http.HandlerFunc.ServeHTTP(...)
        c:/go/src/net/http/server.go:2046
github.com/minio/minio/cmd.httpTraceHdrs.func1({0x50073e0, 0xc0273ec460}, 0x0)
        d:/minio/minio/cmd/handler-utils.go:372 +0x53
net/http.HandlerFunc.ServeHTTP(0x5007380, {0x50073e0, 0xc0273ec460}, 0x10)
        c:/go/src/net/http/server.go:2046 +0x2f
github.com/minio/minio/cmd.addCustomHeaders.func1({0x5007380, 0xc0273dcf00}, 0xc0273f7340)
```
Reverts but adds write checks.
											
										 
											2021-12-03 03:22:32 +08:00
										 |  |  | 					write([]byte{0}) | 
					
						
							| 
									
										
										
										
											2020-05-12 11:41:38 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-07-09 04:51:18 +08:00
										 |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							| 
									
										
										
										
											2020-05-12 11:41:38 +08:00
										 |  |  | 	return func(err error) { | 
					
						
							|  |  |  | 		if doneCh == nil { | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-03-19 07:19:29 +08:00
										 |  |  | 		// Indicate we are ready to write.
 | 
					
						
							| 
									
										
										
										
											2020-05-12 11:41:38 +08:00
										 |  |  | 		doneCh <- err | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-19 07:19:29 +08:00
										 |  |  | 		// Wait for channel to be closed so we don't race on writes.
 | 
					
						
							|  |  |  | 		<-doneCh | 
					
						
							| 
									
										
										
										
											2020-05-12 11:41:38 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// Clear so we can be called multiple times without crashing.
 | 
					
						
							|  |  |  | 		doneCh = nil | 
					
						
							| 
									
										
										
										
											2020-03-19 07:19:29 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // waitForHTTPResponse will wait for responses where keepHTTPResponseAlive
 | 
					
						
							|  |  |  | // has been used.
 | 
					
						
							|  |  |  | // The returned reader contains the payload.
 | 
					
						
							|  |  |  | func waitForHTTPResponse(respBody io.Reader) (io.Reader, error) { | 
					
						
							|  |  |  | 	reader := bufio.NewReader(respBody) | 
					
						
							|  |  |  | 	for { | 
					
						
							|  |  |  | 		b, err := reader.ReadByte() | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-05-12 11:41:38 +08:00
										 |  |  | 		// Check if we have a response ready or a filler byte.
 | 
					
						
							|  |  |  | 		switch b { | 
					
						
							|  |  |  | 		case 0: | 
					
						
							|  |  |  | 			return reader, nil | 
					
						
							|  |  |  | 		case 1: | 
					
						
							| 
									
										
										
										
											2022-09-20 02:05:16 +08:00
										 |  |  | 			errorText, err := io.ReadAll(reader) | 
					
						
							| 
									
										
										
										
											2020-05-12 11:41:38 +08:00
										 |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return nil, err | 
					
						
							| 
									
										
										
										
											2020-03-19 07:19:29 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2020-05-12 11:41:38 +08:00
										 |  |  | 			return nil, errors.New(string(errorText)) | 
					
						
							|  |  |  | 		case 32: | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		default: | 
					
						
							|  |  |  | 			return nil, fmt.Errorf("unexpected filler byte: %d", b) | 
					
						
							| 
									
										
										
										
											2020-03-19 07:19:29 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-07-09 04:51:18 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-29 00:18:35 +08:00
										 |  |  | // httpStreamResponse allows streaming a response, but still send an error.
 | 
					
						
							|  |  |  | type httpStreamResponse struct { | 
					
						
							|  |  |  | 	done  chan error | 
					
						
							|  |  |  | 	block chan []byte | 
					
						
							|  |  |  | 	err   error | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-30 23:26:43 +08:00
										 |  |  | // Write part of the streaming response.
 | 
					
						
							| 
									
										
										
										
											2020-10-29 00:18:35 +08:00
										 |  |  | // Note that upstream errors are currently not forwarded, but may be in the future.
 | 
					
						
							|  |  |  | func (h *httpStreamResponse) Write(b []byte) (int, error) { | 
					
						
							| 
									
										
										
										
											2020-11-12 10:07:40 +08:00
										 |  |  | 	if len(b) == 0 || h.err != nil { | 
					
						
							|  |  |  | 		// Ignore 0 length blocks
 | 
					
						
							|  |  |  | 		return 0, h.err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-10-29 00:18:35 +08:00
										 |  |  | 	tmp := make([]byte, len(b)) | 
					
						
							|  |  |  | 	copy(tmp, b) | 
					
						
							|  |  |  | 	h.block <- tmp | 
					
						
							|  |  |  | 	return len(b), h.err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // CloseWithError will close the stream and return the specified error.
 | 
					
						
							|  |  |  | // This can be done several times, but only the first error will be sent.
 | 
					
						
							|  |  |  | // After calling this the stream should not be written to.
 | 
					
						
							|  |  |  | func (h *httpStreamResponse) CloseWithError(err error) { | 
					
						
							|  |  |  | 	if h.done == nil { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	h.done <- err | 
					
						
							|  |  |  | 	h.err = err | 
					
						
							|  |  |  | 	// Indicates that the response is done.
 | 
					
						
							|  |  |  | 	<-h.done | 
					
						
							|  |  |  | 	h.done = nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // streamHTTPResponse can be used to avoid timeouts with long storage
 | 
					
						
							| 
									
										
										
										
											2021-02-27 07:11:42 +08:00
										 |  |  | // operations, such as bitrot verification or data usage scanning.
 | 
					
						
							| 
									
										
										
										
											2020-10-29 00:18:35 +08:00
										 |  |  | // Every 10 seconds a space character is sent.
 | 
					
						
							|  |  |  | // The returned function should always be called to release resources.
 | 
					
						
							|  |  |  | // An optional error can be sent which will be picked as text only error,
 | 
					
						
							|  |  |  | // without its original type by the receiver.
 | 
					
						
							|  |  |  | // waitForHTTPStream should be used to the receiving side.
 | 
					
						
							|  |  |  | func streamHTTPResponse(w http.ResponseWriter) *httpStreamResponse { | 
					
						
							|  |  |  | 	doneCh := make(chan error) | 
					
						
							|  |  |  | 	blockCh := make(chan []byte) | 
					
						
							|  |  |  | 	h := httpStreamResponse{done: doneCh, block: blockCh} | 
					
						
							|  |  |  | 	go func() { | 
					
						
							| 
									
										
										
										
											2022-01-03 01:15:06 +08:00
										 |  |  | 		canWrite := true | 
					
						
							| 
									
										
										
										
											2021-11-25 01:42:42 +08:00
										 |  |  | 		write := func(b []byte) { | 
					
						
							|  |  |  | 			if canWrite { | 
					
						
							|  |  |  | 				n, err := w.Write(b) | 
					
						
							|  |  |  | 				if err != nil || n != len(b) { | 
					
						
							|  |  |  | 					canWrite = false | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-29 00:18:35 +08:00
										 |  |  | 		ticker := time.NewTicker(time.Second * 10) | 
					
						
							| 
									
										
										
										
											2021-11-19 09:19:58 +08:00
										 |  |  | 		defer ticker.Stop() | 
					
						
							| 
									
										
										
										
											2020-10-29 00:18:35 +08:00
										 |  |  | 		for { | 
					
						
							|  |  |  | 			select { | 
					
						
							|  |  |  | 			case <-ticker.C: | 
					
						
							|  |  |  | 				// Response not ready, write a filler byte.
 | 
					
						
							| 
									
										
										
										
											2021-11-25 01:42:42 +08:00
										 |  |  | 				write([]byte{32}) | 
					
						
							|  |  |  | 				if canWrite { | 
					
						
							|  |  |  | 					w.(http.Flusher).Flush() | 
					
						
							| 
									
										
										
										
											2021-11-19 09:19:58 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2020-10-29 00:18:35 +08:00
										 |  |  | 			case err := <-doneCh: | 
					
						
							|  |  |  | 				if err != nil { | 
					
						
							| 
									
										
										
										
											2021-11-25 01:42:42 +08:00
										 |  |  | 					write([]byte{1}) | 
					
						
							|  |  |  | 					write([]byte(err.Error())) | 
					
						
							| 
									
										
										
										
											2020-10-29 00:18:35 +08:00
										 |  |  | 				} else { | 
					
						
							| 
									
										
										
										
											2021-11-25 01:42:42 +08:00
										 |  |  | 					write([]byte{0}) | 
					
						
							| 
									
										
										
										
											2020-10-29 00:18:35 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2021-11-25 01:42:42 +08:00
										 |  |  | 				close(doneCh) | 
					
						
							| 
									
										
										
										
											2020-10-29 00:18:35 +08:00
										 |  |  | 				return | 
					
						
							|  |  |  | 			case block := <-blockCh: | 
					
						
							|  |  |  | 				var tmp [5]byte | 
					
						
							|  |  |  | 				tmp[0] = 2 | 
					
						
							|  |  |  | 				binary.LittleEndian.PutUint32(tmp[1:], uint32(len(block))) | 
					
						
							| 
									
										
										
										
											2021-11-25 01:42:42 +08:00
										 |  |  | 				write(tmp[:]) | 
					
						
							|  |  |  | 				write(block) | 
					
						
							|  |  |  | 				if canWrite { | 
					
						
							|  |  |  | 					w.(http.Flusher).Flush() | 
					
						
							| 
									
										
										
										
											2021-11-19 09:19:58 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2020-10-29 00:18:35 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 	return &h | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-02 23:11:50 +08:00
										 |  |  | var poolBuf8k = sync.Pool{ | 
					
						
							|  |  |  | 	New: func() interface{} { | 
					
						
							|  |  |  | 		b := make([]byte, 8192) | 
					
						
							|  |  |  | 		return &b | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-19 04:50:21 +08:00
										 |  |  | var poolBuf128k = sync.Pool{ | 
					
						
							|  |  |  | 	New: func() interface{} { | 
					
						
							|  |  |  | 		b := make([]byte, 128<<10) | 
					
						
							|  |  |  | 		return b | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-29 00:18:35 +08:00
										 |  |  | // waitForHTTPStream will wait for responses where
 | 
					
						
							|  |  |  | // streamHTTPResponse has been used.
 | 
					
						
							|  |  |  | // The returned reader contains the payload and must be closed if no error is returned.
 | 
					
						
							|  |  |  | func waitForHTTPStream(respBody io.ReadCloser, w io.Writer) error { | 
					
						
							|  |  |  | 	var tmp [1]byte | 
					
						
							| 
									
										
										
										
											2021-05-08 00:11:05 +08:00
										 |  |  | 	// 8K copy buffer, reused for less allocs...
 | 
					
						
							| 
									
										
										
										
											2021-11-02 23:11:50 +08:00
										 |  |  | 	bufp := poolBuf8k.Get().(*[]byte) | 
					
						
							|  |  |  | 	buf := *bufp | 
					
						
							|  |  |  | 	defer poolBuf8k.Put(bufp) | 
					
						
							| 
									
										
										
										
											2020-10-29 00:18:35 +08:00
										 |  |  | 	for { | 
					
						
							|  |  |  | 		_, err := io.ReadFull(respBody, tmp[:]) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// Check if we have a response ready or a filler byte.
 | 
					
						
							|  |  |  | 		switch tmp[0] { | 
					
						
							|  |  |  | 		case 0: | 
					
						
							|  |  |  | 			// 0 is unbuffered, copy the rest.
 | 
					
						
							| 
									
										
										
										
											2021-11-02 23:11:50 +08:00
										 |  |  | 			_, err := io.CopyBuffer(w, respBody, buf) | 
					
						
							| 
									
										
										
										
											2020-10-29 00:18:35 +08:00
										 |  |  | 			if err == io.EOF { | 
					
						
							|  |  |  | 				return nil | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		case 1: | 
					
						
							| 
									
										
										
										
											2022-09-20 02:05:16 +08:00
										 |  |  | 			errorText, err := io.ReadAll(respBody) | 
					
						
							| 
									
										
										
										
											2020-10-29 00:18:35 +08:00
										 |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return err | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			return errors.New(string(errorText)) | 
					
						
							|  |  |  | 		case 2: | 
					
						
							|  |  |  | 			// Block of data
 | 
					
						
							|  |  |  | 			var tmp [4]byte | 
					
						
							|  |  |  | 			_, err := io.ReadFull(respBody, tmp[:]) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return err | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			length := binary.LittleEndian.Uint32(tmp[:]) | 
					
						
							| 
									
										
										
										
											2022-06-03 00:16:26 +08:00
										 |  |  | 			n, err := io.CopyBuffer(w, io.LimitReader(respBody, int64(length)), buf) | 
					
						
							| 
									
										
										
										
											2020-10-29 00:18:35 +08:00
										 |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return err | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2022-06-03 00:16:26 +08:00
										 |  |  | 			if n != int64(length) { | 
					
						
							|  |  |  | 				return io.ErrUnexpectedEOF | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2020-10-29 00:18:35 +08:00
										 |  |  | 			continue | 
					
						
							|  |  |  | 		case 32: | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		default: | 
					
						
							|  |  |  | 			return fmt.Errorf("unexpected filler byte: %d", tmp[0]) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-09 04:51:18 +08:00
										 |  |  | // VerifyFileResp - VerifyFile()'s response.
 | 
					
						
							|  |  |  | type VerifyFileResp struct { | 
					
						
							|  |  |  | 	Err error | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | // VerifyFileHandler - Verify all part of file for bitrot errors.
 | 
					
						
							|  |  |  | func (s *storageRESTServer) VerifyFileHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							| 
									
										
										
										
											2019-07-09 04:51:18 +08:00
										 |  |  | 	if !s.IsValid(w, r) { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-12-10 00:38:46 +08:00
										 |  |  | 	volume := r.Form.Get(storageRESTVolume) | 
					
						
							|  |  |  | 	filePath := r.Form.Get(storageRESTFilePath) | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if r.ContentLength < 0 { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, errInvalidArgument) | 
					
						
							| 
									
										
										
										
											2019-07-13 07:29:44 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	var fi FileInfo | 
					
						
							| 
									
										
										
										
											2020-11-03 09:07:52 +08:00
										 |  |  | 	if err := msgp.Decode(r.Body, &fi); err != nil { | 
					
						
							| 
									
										
										
										
											2019-07-09 04:51:18 +08:00
										 |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-31 10:45:12 +08:00
										 |  |  | 	setEventStreamHeaders(w) | 
					
						
							| 
									
										
										
										
											2019-10-02 04:12:15 +08:00
										 |  |  | 	encoder := gob.NewEncoder(w) | 
					
						
							| 
									
										
										
										
											2020-03-19 07:19:29 +08:00
										 |  |  | 	done := keepHTTPResponseAlive(w) | 
					
						
							| 
									
										
										
										
											2020-11-03 09:07:52 +08:00
										 |  |  | 	err := s.storage.VerifyFile(r.Context(), volume, filePath, fi) | 
					
						
							| 
									
										
										
										
											2020-05-12 11:41:38 +08:00
										 |  |  | 	done(nil) | 
					
						
							| 
									
										
										
										
											2019-10-02 04:12:15 +08:00
										 |  |  | 	vresp := &VerifyFileResp{} | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2020-01-15 10:45:17 +08:00
										 |  |  | 		vresp.Err = StorageErr(err.Error()) | 
					
						
							| 
									
										
										
										
											2019-10-02 04:12:15 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	encoder.Encode(vresp) | 
					
						
							| 
									
										
										
										
											2019-07-09 04:51:18 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-04 01:25:17 +08:00
										 |  |  | func checkDiskFatalErrs(errs []error) error { | 
					
						
							|  |  |  | 	// This returns a common error if all errors are
 | 
					
						
							|  |  |  | 	// same errors, then there is no point starting
 | 
					
						
							|  |  |  | 	// the server.
 | 
					
						
							|  |  |  | 	if countErrs(errs, errUnsupportedDisk) == len(errs) { | 
					
						
							|  |  |  | 		return errUnsupportedDisk | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if countErrs(errs, errDiskAccessDenied) == len(errs) { | 
					
						
							|  |  |  | 		return errDiskAccessDenied | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if countErrs(errs, errFileAccessDenied) == len(errs) { | 
					
						
							|  |  |  | 		return errDiskAccessDenied | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if countErrs(errs, errDiskNotDir) == len(errs) { | 
					
						
							|  |  |  | 		return errDiskNotDir | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if countErrs(errs, errFaultyDisk) == len(errs) { | 
					
						
							|  |  |  | 		return errFaultyDisk | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-31 01:58:37 +08:00
										 |  |  | 	if countErrs(errs, errXLBackend) == len(errs) { | 
					
						
							|  |  |  | 		return errXLBackend | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-04 01:25:17 +08:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-29 10:39:32 +08:00
										 |  |  | // A single function to write certain errors to be fatal
 | 
					
						
							|  |  |  | // or informative based on the `exit` flag, please look
 | 
					
						
							|  |  |  | // at each implementation of error for added hints.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // FIXME: This is an unusual function but serves its purpose for
 | 
					
						
							|  |  |  | // now, need to revist the overall erroring structure here.
 | 
					
						
							|  |  |  | // Do not like it :-(
 | 
					
						
							|  |  |  | func logFatalErrs(err error, endpoint Endpoint, exit bool) { | 
					
						
							| 
									
										
										
										
											2021-05-16 03:56:58 +08:00
										 |  |  | 	switch { | 
					
						
							| 
									
										
										
										
											2022-05-31 01:58:37 +08:00
										 |  |  | 	case errors.Is(err, errXLBackend): | 
					
						
							|  |  |  | 		logger.Fatal(config.ErrInvalidXLValue(err), "Unable to initialize backend") | 
					
						
							| 
									
										
										
										
											2021-05-16 03:56:58 +08:00
										 |  |  | 	case errors.Is(err, errUnsupportedDisk): | 
					
						
							| 
									
										
										
										
											2020-09-29 10:39:32 +08:00
										 |  |  | 		var hint string | 
					
						
							|  |  |  | 		if endpoint.URL != nil { | 
					
						
							| 
									
										
										
										
											2022-08-05 07:10:08 +08:00
										 |  |  | 			hint = fmt.Sprintf("Drive '%s' does not support O_DIRECT flags, MinIO erasure coding requires filesystems with O_DIRECT support", endpoint.Path) | 
					
						
							| 
									
										
										
										
											2020-09-29 10:39:32 +08:00
										 |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2022-08-05 07:10:08 +08:00
										 |  |  | 			hint = "Drives do not support O_DIRECT flags, MinIO erasure coding requires filesystems with O_DIRECT support" | 
					
						
							| 
									
										
										
										
											2020-09-29 10:39:32 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		logger.Fatal(config.ErrUnsupportedBackend(err).Hint(hint), "Unable to initialize backend") | 
					
						
							| 
									
										
										
										
											2021-05-16 03:56:58 +08:00
										 |  |  | 	case errors.Is(err, errDiskNotDir): | 
					
						
							| 
									
										
										
										
											2020-09-29 10:39:32 +08:00
										 |  |  | 		var hint string | 
					
						
							|  |  |  | 		if endpoint.URL != nil { | 
					
						
							| 
									
										
										
										
											2022-08-05 07:10:08 +08:00
										 |  |  | 			hint = fmt.Sprintf("Drive '%s' is not a directory, MinIO erasure coding needs a directory", endpoint.Path) | 
					
						
							| 
									
										
										
										
											2020-09-29 10:39:32 +08:00
										 |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2022-08-05 07:10:08 +08:00
										 |  |  | 			hint = "Drives are not directories, MinIO erasure coding needs directories" | 
					
						
							| 
									
										
										
										
											2020-09-29 10:39:32 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		logger.Fatal(config.ErrUnableToWriteInBackend(err).Hint(hint), "Unable to initialize backend") | 
					
						
							| 
									
										
										
										
											2021-12-04 01:25:17 +08:00
										 |  |  | 	case errors.Is(err, errDiskAccessDenied): | 
					
						
							| 
									
										
										
										
											2020-09-29 10:39:32 +08:00
										 |  |  | 		// Show a descriptive error with a hint about how to fix it.
 | 
					
						
							|  |  |  | 		var username string | 
					
						
							|  |  |  | 		if u, err := user.Current(); err == nil { | 
					
						
							|  |  |  | 			username = u.Username | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			username = "<your-username>" | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		var hint string | 
					
						
							|  |  |  | 		if endpoint.URL != nil { | 
					
						
							|  |  |  | 			hint = fmt.Sprintf("Run the following command to add write permissions: `sudo chown -R %s %s && sudo chmod u+rxw %s`", | 
					
						
							|  |  |  | 				username, endpoint.Path, endpoint.Path) | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			hint = fmt.Sprintf("Run the following command to add write permissions: `sudo chown -R %s. <path> && sudo chmod u+rxw <path>`", username) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-05-16 03:56:58 +08:00
										 |  |  | 		if !exit { | 
					
						
							| 
									
										
										
										
											2022-08-05 07:10:08 +08:00
										 |  |  | 			logger.LogIf(GlobalContext, fmt.Errorf("Drive is not writable %s, %s", endpoint, hint)) | 
					
						
							| 
									
										
										
										
											2021-05-16 03:56:58 +08:00
										 |  |  | 		} else { | 
					
						
							|  |  |  | 			logger.Fatal(config.ErrUnableToWriteInBackend(err).Hint(hint), "Unable to initialize backend") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	case errors.Is(err, errFaultyDisk): | 
					
						
							| 
									
										
										
										
											2020-09-29 10:39:32 +08:00
										 |  |  | 		if !exit { | 
					
						
							| 
									
										
										
										
											2022-08-05 07:10:08 +08:00
										 |  |  | 			logger.LogIf(GlobalContext, fmt.Errorf("Drive is faulty at %s, please replace the drive - drive will be offline", endpoint)) | 
					
						
							| 
									
										
										
										
											2020-09-29 10:39:32 +08:00
										 |  |  | 		} else { | 
					
						
							|  |  |  | 			logger.Fatal(err, "Unable to initialize backend") | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-05-16 03:56:58 +08:00
										 |  |  | 	case errors.Is(err, errDiskFull): | 
					
						
							| 
									
										
										
										
											2020-09-29 10:39:32 +08:00
										 |  |  | 		if !exit { | 
					
						
							| 
									
										
										
										
											2022-08-05 07:10:08 +08:00
										 |  |  | 			logger.LogIf(GlobalContext, fmt.Errorf("Drive is already full at %s, incoming I/O will fail - drive will be offline", endpoint)) | 
					
						
							| 
									
										
										
										
											2020-09-29 10:39:32 +08:00
										 |  |  | 		} else { | 
					
						
							|  |  |  | 			logger.Fatal(err, "Unable to initialize backend") | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-05-16 03:56:58 +08:00
										 |  |  | 	default: | 
					
						
							| 
									
										
										
										
											2020-09-29 10:39:32 +08:00
										 |  |  | 		if !exit { | 
					
						
							| 
									
										
										
										
											2022-08-05 07:10:08 +08:00
										 |  |  | 			logger.LogIf(GlobalContext, fmt.Errorf("Drive returned an unexpected error at %s, please investigate - drive will be offline (%w)", endpoint, err)) | 
					
						
							| 
									
										
										
										
											2020-09-29 10:39:32 +08:00
										 |  |  | 		} else { | 
					
						
							|  |  |  | 			logger.Fatal(err, "Unable to initialize backend") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-10 02:29:16 +08:00
										 |  |  | // StatInfoFile returns file stat info.
 | 
					
						
							|  |  |  | func (s *storageRESTServer) StatInfoFile(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	if !s.IsValid(w, r) { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-12-10 00:38:46 +08:00
										 |  |  | 	volume := r.Form.Get(storageRESTVolume) | 
					
						
							|  |  |  | 	filePath := r.Form.Get(storageRESTFilePath) | 
					
						
							|  |  |  | 	glob := r.Form.Get(storageRESTGlob) | 
					
						
							| 
									
										
										
										
											2021-07-10 02:29:16 +08:00
										 |  |  | 	done := keepHTTPResponseAlive(w) | 
					
						
							| 
									
										
										
										
											2021-10-02 02:50:00 +08:00
										 |  |  | 	stats, err := s.storage.StatInfoFile(r.Context(), volume, filePath, glob == "true") | 
					
						
							| 
									
										
										
										
											2021-07-10 02:29:16 +08:00
										 |  |  | 	done(err) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-10-02 02:50:00 +08:00
										 |  |  | 	for _, si := range stats { | 
					
						
							|  |  |  | 		msgp.Encode(w, &si) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-07-10 02:29:16 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-19 23:35:29 +08:00
										 |  |  | // ReadMultiple returns multiple files
 | 
					
						
							|  |  |  | func (s *storageRESTServer) ReadMultiple(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	if !s.IsValid(w, r) { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	rw := streamHTTPResponse(w) | 
					
						
							|  |  |  | 	defer func() { | 
					
						
							|  |  |  | 		if r := recover(); r != nil { | 
					
						
							|  |  |  | 			debug.PrintStack() | 
					
						
							|  |  |  | 			rw.CloseWithError(fmt.Errorf("panic: %v", r)) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var req ReadMultipleReq | 
					
						
							|  |  |  | 	mr := msgpNewReader(r.Body) | 
					
						
							|  |  |  | 	err := req.DecodeMsg(mr) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		rw.CloseWithError(err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mw := msgp.NewWriter(rw) | 
					
						
							|  |  |  | 	responses := make(chan ReadMultipleResp, len(req.Files)) | 
					
						
							|  |  |  | 	var wg sync.WaitGroup | 
					
						
							|  |  |  | 	wg.Add(1) | 
					
						
							|  |  |  | 	go func() { | 
					
						
							|  |  |  | 		defer wg.Done() | 
					
						
							|  |  |  | 		for resp := range responses { | 
					
						
							|  |  |  | 			err := resp.EncodeMsg(mw) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				rw.CloseWithError(err) | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			mw.Flush() | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 	err = s.storage.ReadMultiple(r.Context(), req, responses) | 
					
						
							|  |  |  | 	wg.Wait() | 
					
						
							|  |  |  | 	rw.CloseWithError(err) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | // registerStorageRPCRouter - register storage rpc router.
 | 
					
						
							| 
									
										
										
										
											2020-12-02 05:50:33 +08:00
										 |  |  | func registerStorageRESTHandlers(router *mux.Router, endpointServerPools EndpointServerPools) { | 
					
						
							| 
									
										
										
										
											2021-05-19 08:25:00 +08:00
										 |  |  | 	storageDisks := make([][]*xlStorage, len(endpointServerPools)) | 
					
						
							|  |  |  | 	for poolIdx, ep := range endpointServerPools { | 
					
						
							|  |  |  | 		storageDisks[poolIdx] = make([]*xlStorage, len(ep.Endpoints)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	var wg sync.WaitGroup | 
					
						
							|  |  |  | 	for poolIdx, ep := range endpointServerPools { | 
					
						
							|  |  |  | 		for setIdx, endpoint := range ep.Endpoints { | 
					
						
							| 
									
										
										
										
											2019-11-20 09:42:27 +08:00
										 |  |  | 			if !endpoint.IsLocal { | 
					
						
							|  |  |  | 				continue | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2021-05-19 08:25:00 +08:00
										 |  |  | 			wg.Add(1) | 
					
						
							|  |  |  | 			go func(poolIdx, setIdx int, endpoint Endpoint) { | 
					
						
							|  |  |  | 				defer wg.Done() | 
					
						
							|  |  |  | 				var err error | 
					
						
							|  |  |  | 				storageDisks[poolIdx][setIdx], err = newXLStorage(endpoint) | 
					
						
							|  |  |  | 				if err != nil { | 
					
						
							|  |  |  | 					// if supported errors don't fail, we proceed to
 | 
					
						
							|  |  |  | 					// printing message and moving forward.
 | 
					
						
							|  |  |  | 					logFatalErrs(err, endpoint, false) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			}(poolIdx, setIdx, endpoint) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	wg.Wait() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, setDisks := range storageDisks { | 
					
						
							|  |  |  | 		for _, storage := range setDisks { | 
					
						
							|  |  |  | 			if storage == nil { | 
					
						
							|  |  |  | 				continue | 
					
						
							| 
									
										
										
										
											2019-11-20 09:42:27 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-19 08:25:00 +08:00
										 |  |  | 			endpoint := storage.Endpoint() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-10 03:38:54 +08:00
										 |  |  | 			server := &storageRESTServer{storage: newXLStorageDiskIDCheck(storage)} | 
					
						
							|  |  |  | 			server.storage.SetDiskID(storage.diskID) | 
					
						
							| 
									
										
										
										
											2019-11-20 09:42:27 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			subrouter := router.PathPrefix(path.Join(storageRESTPrefix, endpoint.Path)).Subrouter() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-18 05:49:26 +08:00
										 |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodHealth).HandlerFunc(httpTraceHdrs(server.HealthHandler)) | 
					
						
							| 
									
										
										
										
											2019-11-20 09:42:27 +08:00
										 |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodDiskInfo).HandlerFunc(httpTraceHdrs(server.DiskInfoHandler)) | 
					
						
							| 
									
										
										
										
											2021-02-27 07:11:42 +08:00
										 |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodNSScanner).HandlerFunc(httpTraceHdrs(server.NSScannerHandler)) | 
					
						
							| 
									
										
										
										
											2021-12-10 00:38:46 +08:00
										 |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodMakeVol).HandlerFunc(httpTraceHdrs(server.MakeVolHandler)) | 
					
						
							|  |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodMakeVolBulk).HandlerFunc(httpTraceHdrs(server.MakeVolBulkHandler)) | 
					
						
							|  |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodStatVol).HandlerFunc(httpTraceHdrs(server.StatVolHandler)) | 
					
						
							|  |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodDeleteVol).HandlerFunc(httpTraceHdrs(server.DeleteVolHandler)) | 
					
						
							| 
									
										
										
										
											2019-11-20 09:42:27 +08:00
										 |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodListVols).HandlerFunc(httpTraceHdrs(server.ListVolsHandler)) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-10 00:38:46 +08:00
										 |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodAppendFile).HandlerFunc(httpTraceHdrs(server.AppendFileHandler)) | 
					
						
							|  |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodWriteAll).HandlerFunc(httpTraceHdrs(server.WriteAllHandler)) | 
					
						
							|  |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodWriteMetadata).HandlerFunc(httpTraceHdrs(server.WriteMetadataHandler)) | 
					
						
							|  |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodUpdateMetadata).HandlerFunc(httpTraceHdrs(server.UpdateMetadataHandler)) | 
					
						
							|  |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodDeleteVersion).HandlerFunc(httpTraceHdrs(server.DeleteVersionHandler)) | 
					
						
							|  |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodReadVersion).HandlerFunc(httpTraceHdrs(server.ReadVersionHandler)) | 
					
						
							| 
									
										
										
										
											2022-04-21 03:49:05 +08:00
										 |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodReadXL).HandlerFunc(httpTraceHdrs(server.ReadXLHandler)) | 
					
						
							| 
									
										
										
										
											2021-12-10 00:38:46 +08:00
										 |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodRenameData).HandlerFunc(httpTraceHdrs(server.RenameDataHandler)) | 
					
						
							|  |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodCreateFile).HandlerFunc(httpTraceHdrs(server.CreateFileHandler)) | 
					
						
							|  |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodCheckParts).HandlerFunc(httpTraceHdrs(server.CheckPartsHandler)) | 
					
						
							|  |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodReadAll).HandlerFunc(httpTraceHdrs(server.ReadAllHandler)) | 
					
						
							|  |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodReadFile).HandlerFunc(httpTraceHdrs(server.ReadFileHandler)) | 
					
						
							|  |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodReadFileStream).HandlerFunc(httpTraceHdrs(server.ReadFileStreamHandler)) | 
					
						
							|  |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodListDir).HandlerFunc(httpTraceHdrs(server.ListDirHandler)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodDeleteVersions).HandlerFunc(httpTraceHdrs(server.DeleteVersionsHandler)) | 
					
						
							|  |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodDeleteFile).HandlerFunc(httpTraceHdrs(server.DeleteFileHandler)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodRenameFile).HandlerFunc(httpTraceHdrs(server.RenameFileHandler)) | 
					
						
							|  |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodVerifyFile).HandlerFunc(httpTraceHdrs(server.VerifyFileHandler)) | 
					
						
							|  |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodWalkDir).HandlerFunc(httpTraceHdrs(server.WalkDirHandler)) | 
					
						
							|  |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodStatInfoFile).HandlerFunc(httpTraceHdrs(server.StatInfoFile)) | 
					
						
							| 
									
										
										
										
											2022-07-19 23:35:29 +08:00
										 |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodReadMultiple).HandlerFunc(httpTraceHdrs(server.ReadMultiple)) | 
					
						
							| 
									
										
										
										
											2019-11-20 09:42:27 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | } |