| 
									
										
										
										
											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" | 
					
						
							| 
									
										
										
										
											2020-03-19 07:19:29 +08:00
										 |  |  | 	"io/ioutil" | 
					
						
							| 
									
										
										
										
											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" | 
					
						
							|  |  |  | 	"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-06-24 23:41:04 +08:00
										 |  |  | 	jwtreq "github.com/golang-jwt/jwt/request" | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	"github.com/gorilla/mux" | 
					
						
							| 
									
										
										
										
											2021-06-02 05:59:40 +08:00
										 |  |  | 	"github.com/minio/minio/internal/config" | 
					
						
							|  |  |  | 	xhttp "github.com/minio/minio/internal/http" | 
					
						
							|  |  |  | 	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
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-26 01:37:53 +08:00
										 |  |  | var errDiskStale = errors.New("disk 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 { | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	storage *xlStorage | 
					
						
							| 
									
										
										
										
											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-03-18 13:20:26 +08:00
										 |  |  | 	w.(http.Flusher).Flush() | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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.
 | 
					
						
							|  |  |  | func (s *storageRESTServer) IsValid(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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											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) { | 
					
						
							|  |  |  | 	if !s.IsValid(w, r) { | 
					
						
							|  |  |  | 		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
										 |  |  | 	} | 
					
						
							|  |  |  | 	defer w.(http.Flusher).Flush() | 
					
						
							| 
									
										
										
										
											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 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-31 10:45:12 +08:00
										 |  |  | 	setEventStreamHeaders(w) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-19 07:19:29 +08:00
										 |  |  | 	var cache dataUsageCache | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +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-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 | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 	usageInfo, err := s.storage.NSScanner(ctx, cache, updates) | 
					
						
							| 
									
										
										
										
											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 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	vars := mux.Vars(r) | 
					
						
							|  |  |  | 	volume := vars[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 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	vars := mux.Vars(r) | 
					
						
							|  |  |  | 	volumes := strings.Split(vars[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
										 |  |  | 	defer w.(http.Flusher).Flush() | 
					
						
							|  |  |  | 	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 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	vars := mux.Vars(r) | 
					
						
							|  |  |  | 	volume := vars[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
										 |  |  | 	defer w.(http.Flusher).Flush() | 
					
						
							|  |  |  | 	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 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	vars := mux.Vars(r) | 
					
						
							|  |  |  | 	volume := vars[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 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	vars := mux.Vars(r) | 
					
						
							|  |  |  | 	volume := vars[storageRESTVolume] | 
					
						
							|  |  |  | 	filePath := vars[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 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	vars := mux.Vars(r) | 
					
						
							|  |  |  | 	volume := vars[storageRESTVolume] | 
					
						
							|  |  |  | 	filePath := vars[storageRESTFilePath] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | 	fileSizeStr := vars[storageRESTLength] | 
					
						
							|  |  |  | 	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 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	vars := mux.Vars(r) | 
					
						
							|  |  |  | 	volume := vars[storageRESTVolume] | 
					
						
							|  |  |  | 	filePath := vars[storageRESTFilePath] | 
					
						
							| 
									
										
										
										
											2021-02-04 02:33:43 +08:00
										 |  |  | 	forceDelMarker, err := strconv.ParseBool(vars[storageRESTForceDelMarker]) | 
					
						
							|  |  |  | 	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 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	vars := mux.Vars(r) | 
					
						
							|  |  |  | 	volume := vars[storageRESTVolume] | 
					
						
							|  |  |  | 	filePath := vars[storageRESTFilePath] | 
					
						
							|  |  |  | 	versionID := vars[storageRESTVersionID] | 
					
						
							| 
									
										
										
										
											2021-01-08 11:27:31 +08:00
										 |  |  | 	readData, err := strconv.ParseBool(vars[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 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	vars := mux.Vars(r) | 
					
						
							|  |  |  | 	volume := vars[storageRESTVolume] | 
					
						
							|  |  |  | 	filePath := vars[storageRESTFilePath] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	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 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	vars := mux.Vars(r) | 
					
						
							|  |  |  | 	volume := vars[storageRESTVolume] | 
					
						
							|  |  |  | 	filePath := vars[storageRESTFilePath] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	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 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	vars := mux.Vars(r) | 
					
						
							|  |  |  | 	volume := vars[storageRESTVolume] | 
					
						
							|  |  |  | 	filePath := vars[storageRESTFilePath] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	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 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	vars := mux.Vars(r) | 
					
						
							|  |  |  | 	volume := vars[storageRESTVolume] | 
					
						
							|  |  |  | 	filePath := vars[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 { | 
					
						
							| 
									
										
										
										
											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 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	vars := mux.Vars(r) | 
					
						
							|  |  |  | 	volume := vars[storageRESTVolume] | 
					
						
							|  |  |  | 	filePath := vars[storageRESTFilePath] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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 | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											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-03-18 13:20:26 +08:00
										 |  |  | 	w.(http.Flusher).Flush() | 
					
						
							| 
									
										
										
										
											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 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	vars := mux.Vars(r) | 
					
						
							|  |  |  | 	volume := vars[storageRESTVolume] | 
					
						
							|  |  |  | 	filePath := vars[storageRESTFilePath] | 
					
						
							|  |  |  | 	offset, err := strconv.Atoi(vars[storageRESTOffset]) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	length, err := strconv.Atoi(vars[storageRESTLength]) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if offset < 0 || length < 0 { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, errInvalidArgument) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	var verifier *BitrotVerifier | 
					
						
							|  |  |  | 	if vars[storageRESTBitrotAlgo] != "" { | 
					
						
							|  |  |  | 		hashStr := vars[storageRESTBitrotHash] | 
					
						
							|  |  |  | 		var hash []byte | 
					
						
							|  |  |  | 		hash, err = hex.DecodeString(hashStr) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		verifier = NewBitrotVerifier(BitrotAlgorithmFromString(vars[storageRESTBitrotAlgo]), hash) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	buf := make([]byte, length) | 
					
						
							| 
									
										
										
										
											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-03-18 13:20:26 +08:00
										 |  |  | 	w.(http.Flusher).Flush() | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	vars := mux.Vars(r) | 
					
						
							|  |  |  | 	volume := vars[storageRESTVolume] | 
					
						
							|  |  |  | 	filePath := vars[storageRESTFilePath] | 
					
						
							|  |  |  | 	offset, err := strconv.Atoi(vars[storageRESTOffset]) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	length, err := strconv.Atoi(vars[storageRESTLength]) | 
					
						
							|  |  |  | 	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-03-01 07:33:03 +08:00
										 |  |  | 	if _, err = io.Copy(w, rc); err != nil { | 
					
						
							|  |  |  | 		if !xnet.IsNetworkOrHostDown(err, true) { // do not need to log disconnected clients
 | 
					
						
							|  |  |  | 			logger.LogIf(r.Context(), err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-03-18 13:20:26 +08:00
										 |  |  | 	w.(http.Flusher).Flush() | 
					
						
							| 
									
										
										
										
											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 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	vars := mux.Vars(r) | 
					
						
							|  |  |  | 	volume := vars[storageRESTVolume] | 
					
						
							|  |  |  | 	dirPath := vars[storageRESTDirPath] | 
					
						
							|  |  |  | 	count, err := strconv.Atoi(vars[storageRESTCount]) | 
					
						
							|  |  |  | 	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) | 
					
						
							| 
									
										
										
										
											2019-10-02 04:12:15 +08:00
										 |  |  | 	w.(http.Flusher).Flush() | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // DeleteFileHandler - delete a file.
 | 
					
						
							|  |  |  | func (s *storageRESTServer) DeleteFileHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	if !s.IsValid(w, r) { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	vars := mux.Vars(r) | 
					
						
							|  |  |  | 	volume := vars[storageRESTVolume] | 
					
						
							|  |  |  | 	filePath := vars[storageRESTFilePath] | 
					
						
							| 
									
										
										
										
											2020-10-29 00:18:35 +08:00
										 |  |  | 	recursive, err := strconv.ParseBool(vars[storageRESTRecursive]) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-29 00:18:35 +08:00
										 |  |  | 	err = s.storage.Delete(r.Context(), volume, filePath, recursive) | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	versions := make([]FileInfo, 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) | 
					
						
							|  |  |  | 	w.(http.Flusher).Flush() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	vars := mux.Vars(r) | 
					
						
							|  |  |  | 	srcVolume := vars[storageRESTSrcVolume] | 
					
						
							|  |  |  | 	srcFilePath := vars[storageRESTSrcPath] | 
					
						
							|  |  |  | 	dstVolume := vars[storageRESTDstVolume] | 
					
						
							|  |  |  | 	dstFilePath := vars[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 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	err := s.storage.RenameData(r.Context(), srcVolume, srcFilePath, fi, dstVolume, dstFilePath) | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2020-03-11 23:56:36 +08:00
										 |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											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 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	vars := mux.Vars(r) | 
					
						
							|  |  |  | 	srcVolume := vars[storageRESTSrcVolume] | 
					
						
							|  |  |  | 	srcFilePath := vars[storageRESTSrcPath] | 
					
						
							|  |  |  | 	dstVolume := vars[storageRESTDstVolume] | 
					
						
							|  |  |  | 	dstFilePath := vars[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() { | 
					
						
							|  |  |  | 		// Wait for body to be read.
 | 
					
						
							|  |  |  | 		select { | 
					
						
							|  |  |  | 		case <-ctx.Done(): | 
					
						
							|  |  |  | 		case <-bodyDoneCh: | 
					
						
							|  |  |  | 		case err := <-doneCh: | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				w.Write([]byte{1}) | 
					
						
							|  |  |  | 				w.Write([]byte(err.Error())) | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				w.Write([]byte{0}) | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2021-08-30 23:46:46 +08:00
										 |  |  | 			close(doneCh) | 
					
						
							| 
									
										
										
										
											2021-08-28 00:16:36 +08:00
										 |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		defer close(doneCh) | 
					
						
							|  |  |  | 		// 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.
 | 
					
						
							|  |  |  | 				w.Write([]byte{32}) | 
					
						
							|  |  |  | 				w.(http.Flusher).Flush() | 
					
						
							|  |  |  | 			case err := <-doneCh: | 
					
						
							|  |  |  | 				if err != nil { | 
					
						
							|  |  |  | 					w.Write([]byte{1}) | 
					
						
							|  |  |  | 					w.Write([]byte(err.Error())) | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					w.Write([]byte{0}) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				ticker.Stop() | 
					
						
							|  |  |  | 				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() { | 
					
						
							| 
									
										
										
										
											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) | 
					
						
							|  |  |  | 		for { | 
					
						
							|  |  |  | 			select { | 
					
						
							|  |  |  | 			case <-ticker.C: | 
					
						
							| 
									
										
										
										
											2020-05-12 11:41:38 +08:00
										 |  |  | 				// Response not ready, write a filler byte.
 | 
					
						
							|  |  |  | 				w.Write([]byte{32}) | 
					
						
							| 
									
										
										
										
											2019-07-09 04:51:18 +08:00
										 |  |  | 				w.(http.Flusher).Flush() | 
					
						
							| 
									
										
										
										
											2020-05-12 11:41:38 +08:00
										 |  |  | 			case err := <-doneCh: | 
					
						
							|  |  |  | 				if err != nil { | 
					
						
							|  |  |  | 					w.Write([]byte{1}) | 
					
						
							|  |  |  | 					w.Write([]byte(err.Error())) | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					w.Write([]byte{0}) | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-07-09 04:51:18 +08:00
										 |  |  | 				ticker.Stop() | 
					
						
							|  |  |  | 				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: | 
					
						
							|  |  |  | 			errorText, err := ioutil.ReadAll(reader) | 
					
						
							|  |  |  | 			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 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Write part of the the streaming response.
 | 
					
						
							|  |  |  | // 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() { | 
					
						
							|  |  |  | 		ticker := time.NewTicker(time.Second * 10) | 
					
						
							|  |  |  | 		for { | 
					
						
							|  |  |  | 			select { | 
					
						
							|  |  |  | 			case <-ticker.C: | 
					
						
							|  |  |  | 				// Response not ready, write a filler byte.
 | 
					
						
							|  |  |  | 				w.Write([]byte{32}) | 
					
						
							|  |  |  | 				w.(http.Flusher).Flush() | 
					
						
							|  |  |  | 			case err := <-doneCh: | 
					
						
							|  |  |  | 				ticker.Stop() | 
					
						
							|  |  |  | 				defer close(doneCh) | 
					
						
							|  |  |  | 				if err != nil { | 
					
						
							| 
									
										
										
										
											2020-11-26 04:42:48 +08:00
										 |  |  | 					w.Write([]byte{1}) | 
					
						
							|  |  |  | 					w.Write([]byte(err.Error())) | 
					
						
							| 
									
										
										
										
											2020-10-29 00:18:35 +08:00
										 |  |  | 				} else { | 
					
						
							|  |  |  | 					w.Write([]byte{0}) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			case block := <-blockCh: | 
					
						
							|  |  |  | 				var tmp [5]byte | 
					
						
							|  |  |  | 				tmp[0] = 2 | 
					
						
							|  |  |  | 				binary.LittleEndian.PutUint32(tmp[1:], uint32(len(block))) | 
					
						
							|  |  |  | 				w.Write(tmp[:]) | 
					
						
							|  |  |  | 				w.Write(block) | 
					
						
							|  |  |  | 				w.(http.Flusher).Flush() | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 	return &h | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // 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...
 | 
					
						
							|  |  |  | 	var buf [8 << 10]byte | 
					
						
							| 
									
										
										
										
											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-05-08 00:11:05 +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: | 
					
						
							|  |  |  | 			errorText, err := ioutil.ReadAll(respBody) | 
					
						
							|  |  |  | 			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[:]) | 
					
						
							| 
									
										
										
										
											2021-05-08 00:11:05 +08:00
										 |  |  | 			_, err = io.CopyBuffer(w, io.LimitReader(respBody, int64(length)), buf[:]) | 
					
						
							| 
									
										
										
										
											2020-10-29 00:18:35 +08:00
										 |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return err | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			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 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	vars := mux.Vars(r) | 
					
						
							|  |  |  | 	volume := vars[storageRESTVolume] | 
					
						
							|  |  |  | 	filePath := vars[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) | 
					
						
							|  |  |  | 	w.(http.Flusher).Flush() | 
					
						
							| 
									
										
										
										
											2019-07-09 04:51:18 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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 { | 
					
						
							|  |  |  | 	case errors.Is(err, errMinDiskSize): | 
					
						
							| 
									
										
										
										
											2020-09-29 10:39:32 +08:00
										 |  |  | 		logger.Fatal(config.ErrUnableToWriteInBackend(err).Hint(err.Error()), "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 { | 
					
						
							|  |  |  | 			hint = fmt.Sprintf("Disk '%s' does not support O_DIRECT flags, MinIO erasure coding requires filesystems with O_DIRECT support", endpoint.Path) | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			hint = "Disks do not support O_DIRECT flags, MinIO erasure coding requires filesystems with O_DIRECT support" | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		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 { | 
					
						
							|  |  |  | 			hint = fmt.Sprintf("Disk '%s' is not a directory, MinIO erasure coding needs a directory", endpoint.Path) | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			hint = "Disks are not directories, MinIO erasure coding needs directories" | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		logger.Fatal(config.ErrUnableToWriteInBackend(err).Hint(hint), "Unable to initialize backend") | 
					
						
							| 
									
										
										
										
											2021-05-16 03:56:58 +08:00
										 |  |  | 	case errors.Is(err, errFileAccessDenied): | 
					
						
							| 
									
										
										
										
											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 { | 
					
						
							|  |  |  | 			logger.LogIf(GlobalContext, fmt.Errorf("disk is not writable %s, %s", endpoint, hint)) | 
					
						
							|  |  |  | 		} 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 { | 
					
						
							|  |  |  | 			logger.LogIf(GlobalContext, fmt.Errorf("disk is faulty at %s, please replace the drive - disk will be offline", endpoint)) | 
					
						
							|  |  |  | 		} 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 { | 
					
						
							|  |  |  | 			logger.LogIf(GlobalContext, fmt.Errorf("disk is already full at %s, incoming I/O will fail - disk will be offline", endpoint)) | 
					
						
							|  |  |  | 		} 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 { | 
					
						
							| 
									
										
										
										
											2021-05-16 03:56:58 +08:00
										 |  |  | 			logger.LogIf(GlobalContext, fmt.Errorf("disk returned an unexpected error at %s, please investigate - disk 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 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	vars := mux.Vars(r) | 
					
						
							|  |  |  | 	volume := vars[storageRESTVolume] | 
					
						
							|  |  |  | 	filePath := vars[storageRESTFilePath] | 
					
						
							|  |  |  | 	done := keepHTTPResponseAlive(w) | 
					
						
							|  |  |  | 	si, err := s.storage.StatInfoFile(r.Context(), volume, filePath) | 
					
						
							|  |  |  | 	done(err) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	msgp.Encode(w, &si) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-20 09:42:27 +08:00
										 |  |  | 			server := &storageRESTServer{storage: storage} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			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)) | 
					
						
							| 
									
										
										
										
											2019-11-20 09:42:27 +08:00
										 |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodMakeVol).HandlerFunc(httpTraceHdrs(server.MakeVolHandler)).Queries(restQueries(storageRESTVolume)...) | 
					
						
							| 
									
										
										
										
											2019-12-24 08:31:03 +08:00
										 |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodMakeVolBulk).HandlerFunc(httpTraceHdrs(server.MakeVolBulkHandler)).Queries(restQueries(storageRESTVolumes)...) | 
					
						
							| 
									
										
										
										
											2019-11-20 09:42:27 +08:00
										 |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodStatVol).HandlerFunc(httpTraceHdrs(server.StatVolHandler)).Queries(restQueries(storageRESTVolume)...) | 
					
						
							|  |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodDeleteVol).HandlerFunc(httpTraceHdrs(server.DeleteVolHandler)).Queries(restQueries(storageRESTVolume)...) | 
					
						
							|  |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodListVols).HandlerFunc(httpTraceHdrs(server.ListVolsHandler)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodAppendFile).HandlerFunc(httpTraceHdrs(server.AppendFileHandler)). | 
					
						
							|  |  |  | 				Queries(restQueries(storageRESTVolume, storageRESTFilePath)...) | 
					
						
							|  |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodWriteAll).HandlerFunc(httpTraceHdrs(server.WriteAllHandler)). | 
					
						
							|  |  |  | 				Queries(restQueries(storageRESTVolume, storageRESTFilePath)...) | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodWriteMetadata).HandlerFunc(httpTraceHdrs(server.WriteMetadataHandler)). | 
					
						
							|  |  |  | 				Queries(restQueries(storageRESTVolume, storageRESTFilePath)...) | 
					
						
							| 
									
										
										
										
											2021-04-05 04:32:31 +08:00
										 |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodUpdateMetadata).HandlerFunc(httpTraceHdrs(server.UpdateMetadataHandler)). | 
					
						
							|  |  |  | 				Queries(restQueries(storageRESTVolume, storageRESTFilePath)...) | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodDeleteVersion).HandlerFunc(httpTraceHdrs(server.DeleteVersionHandler)). | 
					
						
							| 
									
										
										
										
											2021-02-04 02:33:43 +08:00
										 |  |  | 				Queries(restQueries(storageRESTVolume, storageRESTFilePath, storageRESTForceDelMarker)...) | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodReadVersion).HandlerFunc(httpTraceHdrs(server.ReadVersionHandler)). | 
					
						
							| 
									
										
										
										
											2021-01-08 11:27:31 +08:00
										 |  |  | 				Queries(restQueries(storageRESTVolume, storageRESTFilePath, storageRESTVersionID, storageRESTReadData)...) | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodRenameData).HandlerFunc(httpTraceHdrs(server.RenameDataHandler)). | 
					
						
							| 
									
										
										
										
											2021-04-21 01:44:39 +08:00
										 |  |  | 				Queries(restQueries(storageRESTSrcVolume, storageRESTSrcPath, | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 					storageRESTDstVolume, storageRESTDstPath)...) | 
					
						
							| 
									
										
										
										
											2019-11-20 09:42:27 +08:00
										 |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodCreateFile).HandlerFunc(httpTraceHdrs(server.CreateFileHandler)). | 
					
						
							|  |  |  | 				Queries(restQueries(storageRESTVolume, storageRESTFilePath, storageRESTLength)...) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodCheckParts).HandlerFunc(httpTraceHdrs(server.CheckPartsHandler)). | 
					
						
							|  |  |  | 				Queries(restQueries(storageRESTVolume, storageRESTFilePath)...) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-20 09:42:27 +08:00
										 |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodReadAll).HandlerFunc(httpTraceHdrs(server.ReadAllHandler)). | 
					
						
							|  |  |  | 				Queries(restQueries(storageRESTVolume, storageRESTFilePath)...) | 
					
						
							|  |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodReadFile).HandlerFunc(httpTraceHdrs(server.ReadFileHandler)). | 
					
						
							|  |  |  | 				Queries(restQueries(storageRESTVolume, storageRESTFilePath, storageRESTOffset, storageRESTLength, storageRESTBitrotAlgo, storageRESTBitrotHash)...) | 
					
						
							|  |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodReadFileStream).HandlerFunc(httpTraceHdrs(server.ReadFileStreamHandler)). | 
					
						
							|  |  |  | 				Queries(restQueries(storageRESTVolume, storageRESTFilePath, storageRESTOffset, storageRESTLength)...) | 
					
						
							|  |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodListDir).HandlerFunc(httpTraceHdrs(server.ListDirHandler)). | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 				Queries(restQueries(storageRESTVolume, storageRESTDirPath, storageRESTCount)...) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodDeleteVersions).HandlerFunc(httpTraceHdrs(server.DeleteVersionsHandler)). | 
					
						
							|  |  |  | 				Queries(restQueries(storageRESTVolume, storageRESTTotalVersions)...) | 
					
						
							| 
									
										
										
										
											2019-11-20 09:42:27 +08:00
										 |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodDeleteFile).HandlerFunc(httpTraceHdrs(server.DeleteFileHandler)). | 
					
						
							| 
									
										
										
										
											2020-10-29 00:18:35 +08:00
										 |  |  | 				Queries(restQueries(storageRESTVolume, storageRESTFilePath, storageRESTRecursive)...) | 
					
						
							| 
									
										
										
										
											2019-11-20 09:42:27 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodRenameFile).HandlerFunc(httpTraceHdrs(server.RenameFileHandler)). | 
					
						
							|  |  |  | 				Queries(restQueries(storageRESTSrcVolume, storageRESTSrcPath, storageRESTDstVolume, storageRESTDstPath)...) | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodVerifyFile).HandlerFunc(httpTraceHdrs(server.VerifyFileHandler)). | 
					
						
							|  |  |  | 				Queries(restQueries(storageRESTVolume, storageRESTFilePath)...) | 
					
						
							| 
									
										
										
										
											2020-10-29 00:18:35 +08:00
										 |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodWalkDir).HandlerFunc(httpTraceHdrs(server.WalkDirHandler)). | 
					
						
							| 
									
										
										
										
											2020-11-19 04:03:16 +08:00
										 |  |  | 				Queries(restQueries(storageRESTVolume, storageRESTDirPath, storageRESTRecursive)...) | 
					
						
							| 
									
										
										
										
											2021-07-10 02:29:16 +08:00
										 |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodStatInfoFile).HandlerFunc(httpTraceHdrs(server.StatInfoFile)). | 
					
						
							|  |  |  | 				Queries(restQueries(storageRESTVolume, storageRESTFilePath)...) | 
					
						
							| 
									
										
										
										
											2019-11-20 09:42:27 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | } |