| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | /* | 
					
						
							| 
									
										
										
										
											2019-04-10 02:39:42 +08:00
										 |  |  |  * MinIO Cloud Storage, (C) 2018 MinIO, Inc. | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  |  * | 
					
						
							|  |  |  |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
					
						
							|  |  |  |  * you may not use this file except in compliance with the License. | 
					
						
							|  |  |  |  * You may obtain a copy of the License at | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *     http://www.apache.org/licenses/LICENSE-2.0
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Unless required by applicable law or agreed to in writing, software | 
					
						
							|  |  |  |  * distributed under the License is distributed on an "AS IS" BASIS, | 
					
						
							|  |  |  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
					
						
							|  |  |  |  * See the License for the specific language governing permissions and | 
					
						
							|  |  |  |  * limitations under the License. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | package cmd | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2020-02-02 10:11:29 +08:00
										 |  |  | 	"bufio" | 
					
						
							| 
									
										
										
										
											2019-05-15 04:49:10 +08:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											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" | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	"time" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-31 10:59:22 +08:00
										 |  |  | 	jwtreq "github.com/dgrijalva/jwt-go/request" | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	"github.com/gorilla/mux" | 
					
						
							| 
									
										
										
										
											2019-10-05 01:35:33 +08:00
										 |  |  | 	"github.com/minio/minio/cmd/config" | 
					
						
							| 
									
										
										
										
											2019-07-03 13:34:32 +08:00
										 |  |  | 	xhttp "github.com/minio/minio/cmd/http" | 
					
						
							| 
									
										
										
										
											2020-01-31 10:59:22 +08:00
										 |  |  | 	xjwt "github.com/minio/minio/cmd/jwt" | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	"github.com/minio/minio/cmd/logger" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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 { | 
					
						
							|  |  |  | 	storage *posix | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (s *storageRESTServer) writeErrorResponse(w http.ResponseWriter, err error) { | 
					
						
							|  |  |  | 	w.WriteHeader(http.StatusForbidden) | 
					
						
							|  |  |  | 	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 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if claims.Audience != r.URL.Query().Encode() { | 
					
						
							|  |  |  | 		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 { | 
					
						
							|  |  |  | 	if err := storageServerRequestValidate(r); err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return false | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-10-26 01:37:53 +08:00
										 |  |  | 	diskID := r.URL.Query().Get(storageRESTDiskID) | 
					
						
							|  |  |  | 	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-03-28 05:48:30 +08:00
										 |  |  | 	storedDiskID, err := s.storage.GetDiskID() | 
					
						
							| 
									
										
										
										
											2019-10-26 01:37:53 +08:00
										 |  |  | 	if err == nil && diskID == storedDiskID { | 
					
						
							|  |  |  | 		// 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
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-10-26 01:37:53 +08:00
										 |  |  | 	s.writeErrorResponse(w, errDiskStale) | 
					
						
							|  |  |  | 	return false | 
					
						
							| 
									
										
										
										
											2019-02-14 07:29:46 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	info, err := s.storage.DiskInfo() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	defer w.(http.Flusher).Flush() | 
					
						
							|  |  |  | 	gob.NewEncoder(w).Encode(info) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-12 22:02:37 +08:00
										 |  |  | func (s *storageRESTServer) CrawlAndGetDataUsageHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	if !s.IsValid(w, r) { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	w.Header().Set(xhttp.ContentType, "text/event-stream") | 
					
						
							| 
									
										
										
										
											2020-03-19 07:19:29 +08:00
										 |  |  | 	b, err := ioutil.ReadAll(r.Body) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	var cache dataUsageCache | 
					
						
							|  |  |  | 	err = cache.deserialize(b) | 
					
						
							| 
									
										
										
										
											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 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-19 07:19:29 +08:00
										 |  |  | 	done := keepHTTPResponseAlive(w) | 
					
						
							|  |  |  | 	usageInfo, err := s.storage.CrawlAndGetDataUsage(r.Context(), cache) | 
					
						
							|  |  |  | 	done() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	w.Write(usageInfo.serialize()) | 
					
						
							| 
									
										
										
										
											2019-12-12 22:02:37 +08:00
										 |  |  | 	w.(http.Flusher).Flush() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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] | 
					
						
							|  |  |  | 	err := s.storage.MakeVol(volume) | 
					
						
							|  |  |  | 	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], ",") | 
					
						
							|  |  |  | 	err := s.storage.MakeVolBulk(volumes...) | 
					
						
							|  |  |  | 	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 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	infos, err := s.storage.ListVols() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	gob.NewEncoder(w).Encode(&infos) | 
					
						
							| 
									
										
										
										
											2019-10-02 04:12:15 +08:00
										 |  |  | 	w.(http.Flusher).Flush() | 
					
						
							| 
									
										
										
										
											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] | 
					
						
							|  |  |  | 	info, err := s.storage.StatVol(volume) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	gob.NewEncoder(w).Encode(info) | 
					
						
							| 
									
										
										
										
											2019-10-02 04:12:15 +08:00
										 |  |  | 	w.(http.Flusher).Flush() | 
					
						
							| 
									
										
										
										
											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] | 
					
						
							| 
									
										
										
										
											2020-03-28 12:52:59 +08:00
										 |  |  | 	forceDelete := vars[storageRESTForceDelete] == "true" | 
					
						
							|  |  |  | 	err := s.storage.DeleteVol(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 | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | 	err = s.storage.AppendFile(volume, filePath, buf) | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | // CreateFileHandler - fallocate() space for a file and copy the contents from the request.
 | 
					
						
							|  |  |  | 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 | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | 	err = s.storage.CreateFile(volume, filePath, int64(fileSize), r.Body) | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	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 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-23 04:47:15 +08:00
										 |  |  | 	err := s.storage.WriteAll(volume, filePath, io.LimitReader(r.Body, r.ContentLength)) | 
					
						
							| 
									
										
										
										
											2018-11-14 22:18:35 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | // StatFileHandler - stat a file.
 | 
					
						
							|  |  |  | func (s *storageRESTServer) StatFileHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	if !s.IsValid(w, r) { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	vars := mux.Vars(r) | 
					
						
							|  |  |  | 	volume := vars[storageRESTVolume] | 
					
						
							|  |  |  | 	filePath := vars[storageRESTFilePath] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	info, err := s.storage.StatFile(volume, filePath) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	gob.NewEncoder(w).Encode(info) | 
					
						
							| 
									
										
										
										
											2019-10-02 04:12:15 +08:00
										 |  |  | 	w.(http.Flusher).Flush() | 
					
						
							| 
									
										
										
										
											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] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	buf, err := s.storage.ReadAll(volume, filePath) | 
					
						
							|  |  |  | 	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) | 
					
						
							|  |  |  | 	_, err = s.storage.ReadFile(volume, filePath, int64(offset), buf, verifier) | 
					
						
							|  |  |  | 	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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | 	rc, err := s.storage.ReadFileStream(volume, filePath, int64(offset), int64(length)) | 
					
						
							|  |  |  | 	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)) | 
					
						
							| 
									
										
										
										
											2019-03-18 13:20:26 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | 	io.Copy(w, rc) | 
					
						
							| 
									
										
										
										
											2019-03-18 13:20:26 +08:00
										 |  |  | 	w.(http.Flusher).Flush() | 
					
						
							| 
									
										
										
										
											2019-09-26 14:08:24 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-15 04:49:10 +08:00
										 |  |  | // readMetadata func provides the function types for reading leaf metadata.
 | 
					
						
							|  |  |  | type readMetadataFunc func(buf []byte, volume, entry string) FileInfo | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func readMetadata(buf []byte, volume, entry string) FileInfo { | 
					
						
							|  |  |  | 	m, err := xlMetaV1UnmarshalJSON(context.Background(), buf) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return FileInfo{} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return FileInfo{ | 
					
						
							|  |  |  | 		Volume:   volume, | 
					
						
							|  |  |  | 		Name:     entry, | 
					
						
							|  |  |  | 		ModTime:  m.Stat.ModTime, | 
					
						
							|  |  |  | 		Size:     m.Stat.Size, | 
					
						
							|  |  |  | 		Metadata: m.Meta, | 
					
						
							|  |  |  | 		Parts:    m.Parts, | 
					
						
							|  |  |  | 		Quorum:   m.Erasure.DataBlocks, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-23 10:23:47 +08:00
										 |  |  | // WalkHandler - remote caller to start walking at a requested directory path.
 | 
					
						
							|  |  |  | func (s *storageRESTServer) WalkSplunkHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	if !s.IsValid(w, r) { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	vars := mux.Vars(r) | 
					
						
							|  |  |  | 	volume := vars[storageRESTVolume] | 
					
						
							|  |  |  | 	dirPath := vars[storageRESTDirPath] | 
					
						
							|  |  |  | 	markerPath := vars[storageRESTMarkerPath] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	w.Header().Set(xhttp.ContentType, "text/event-stream") | 
					
						
							|  |  |  | 	encoder := gob.NewEncoder(w) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	fch, err := s.storage.WalkSplunk(volume, dirPath, markerPath, r.Context().Done()) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	for fi := range fch { | 
					
						
							|  |  |  | 		encoder.Encode(&fi) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	w.(http.Flusher).Flush() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-15 04:49:10 +08:00
										 |  |  | // WalkHandler - remote caller to start walking at a requested directory path.
 | 
					
						
							|  |  |  | func (s *storageRESTServer) WalkHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	if !s.IsValid(w, r) { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	vars := mux.Vars(r) | 
					
						
							|  |  |  | 	volume := vars[storageRESTVolume] | 
					
						
							|  |  |  | 	dirPath := vars[storageRESTDirPath] | 
					
						
							|  |  |  | 	markerPath := vars[storageRESTMarkerPath] | 
					
						
							|  |  |  | 	recursive, err := strconv.ParseBool(vars[storageRESTRecursive]) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	leafFile := vars[storageRESTLeafFile] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-22 13:10:13 +08:00
										 |  |  | 	w.Header().Set(xhttp.ContentType, "text/event-stream") | 
					
						
							|  |  |  | 	encoder := gob.NewEncoder(w) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-25 23:52:28 +08:00
										 |  |  | 	fch, err := s.storage.Walk(volume, dirPath, markerPath, recursive, leafFile, readMetadata, r.Context().Done()) | 
					
						
							| 
									
										
										
										
											2019-05-15 04:49:10 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2020-03-23 10:23:47 +08:00
										 |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							| 
									
										
										
										
											2019-05-15 04:49:10 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	for fi := range fch { | 
					
						
							|  |  |  | 		encoder.Encode(&fi) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-10-02 04:12:15 +08:00
										 |  |  | 	w.(http.Flusher).Flush() | 
					
						
							| 
									
										
										
										
											2019-05-15 04:49:10 +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] | 
					
						
							| 
									
										
										
										
											2019-04-24 05:54:28 +08:00
										 |  |  | 	leafFile := vars[storageRESTLeafFile] | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	count, err := strconv.Atoi(vars[storageRESTCount]) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-03-22 13:10:13 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-24 05:54:28 +08:00
										 |  |  | 	entries, err := s.storage.ListDir(volume, dirPath, count, leafFile) | 
					
						
							| 
									
										
										
										
											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] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	err := s.storage.DeleteFile(volume, filePath) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-01 10:01:28 +08:00
										 |  |  | // DeleteFileBulkErrsResp - collection of deleteFile errors
 | 
					
						
							|  |  |  | // for bulk deletes
 | 
					
						
							|  |  |  | type DeleteFileBulkErrsResp struct { | 
					
						
							|  |  |  | 	Errs []error | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-14 03:25:49 +08:00
										 |  |  | // DeleteFileBulkHandler - delete a file.
 | 
					
						
							|  |  |  | func (s *storageRESTServer) DeleteFileBulkHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	if !s.IsValid(w, r) { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	vars := r.URL.Query() | 
					
						
							|  |  |  | 	volume := vars.Get(storageRESTVolume) | 
					
						
							| 
									
										
										
										
											2020-02-02 10:11:29 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	bio := bufio.NewScanner(r.Body) | 
					
						
							|  |  |  | 	var filePaths []string | 
					
						
							|  |  |  | 	for bio.Scan() { | 
					
						
							|  |  |  | 		filePaths = append(filePaths, bio.Text()) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if err := bio.Err(); err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-05-14 03:25:49 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-22 13:10:13 +08:00
										 |  |  | 	dErrsResp := &DeleteFileBulkErrsResp{Errs: make([]error, len(filePaths))} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-11 23:56:36 +08:00
										 |  |  | 	w.Header().Set(xhttp.ContentType, "text/event-stream") | 
					
						
							|  |  |  | 	encoder := gob.NewEncoder(w) | 
					
						
							| 
									
										
										
										
											2020-03-19 07:19:29 +08:00
										 |  |  | 	done := keepHTTPResponseAlive(w) | 
					
						
							| 
									
										
										
										
											2019-05-14 03:25:49 +08:00
										 |  |  | 	errs, err := s.storage.DeleteFileBulk(volume, filePaths) | 
					
						
							| 
									
										
										
										
											2020-03-19 07:19:29 +08:00
										 |  |  | 	done() | 
					
						
							| 
									
										
										
										
											2019-05-14 03:25:49 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-22 13:10:13 +08:00
										 |  |  | 	for idx := range filePaths { | 
					
						
							| 
									
										
										
										
											2019-10-01 10:01:28 +08:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2020-03-11 23:56:36 +08:00
										 |  |  | 			dErrsResp.Errs[idx] = StorageErr(err.Error()) | 
					
						
							| 
									
										
										
										
											2020-03-22 13:10:13 +08:00
										 |  |  | 		} else { | 
					
						
							|  |  |  | 			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() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // DeletePrefixesErrsResp - collection of delete errors
 | 
					
						
							|  |  |  | // for bulk prefixes deletes
 | 
					
						
							|  |  |  | type DeletePrefixesErrsResp struct { | 
					
						
							|  |  |  | 	Errs []error | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // DeletePrefixesHandler - delete a set of a prefixes.
 | 
					
						
							|  |  |  | func (s *storageRESTServer) DeletePrefixesHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	if !s.IsValid(w, r) { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	vars := r.URL.Query() | 
					
						
							|  |  |  | 	volume := vars.Get(storageRESTVolume) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	bio := bufio.NewScanner(r.Body) | 
					
						
							|  |  |  | 	var prefixes []string | 
					
						
							|  |  |  | 	for bio.Scan() { | 
					
						
							|  |  |  | 		prefixes = append(prefixes, bio.Text()) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if err := bio.Err(); err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-22 13:10:13 +08:00
										 |  |  | 	dErrsResp := &DeletePrefixesErrsResp{Errs: make([]error, len(prefixes))} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-11 23:56:36 +08:00
										 |  |  | 	w.Header().Set(xhttp.ContentType, "text/event-stream") | 
					
						
							|  |  |  | 	encoder := gob.NewEncoder(w) | 
					
						
							| 
									
										
										
										
											2020-03-19 07:19:29 +08:00
										 |  |  | 	done := keepHTTPResponseAlive(w) | 
					
						
							| 
									
										
										
										
											2020-03-11 23:56:36 +08:00
										 |  |  | 	errs, err := s.storage.DeletePrefixes(volume, prefixes) | 
					
						
							| 
									
										
										
										
											2020-03-19 07:19:29 +08:00
										 |  |  | 	done() | 
					
						
							| 
									
										
										
										
											2020-03-22 13:10:13 +08:00
										 |  |  | 	for idx := range prefixes { | 
					
						
							| 
									
										
										
										
											2020-03-11 23:56:36 +08:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			dErrsResp.Errs[idx] = StorageErr(err.Error()) | 
					
						
							| 
									
										
										
										
											2020-03-22 13:10:13 +08:00
										 |  |  | 		} else { | 
					
						
							|  |  |  | 			if errs[idx] != nil { | 
					
						
							|  |  |  | 				dErrsResp.Errs[idx] = StorageErr(errs[idx].Error()) | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2020-03-11 23:56:36 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	encoder.Encode(dErrsResp) | 
					
						
							| 
									
										
										
										
											2019-10-01 10:01:28 +08:00
										 |  |  | 	w.(http.Flusher).Flush() | 
					
						
							| 
									
										
										
										
											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] | 
					
						
							|  |  |  | 	err := s.storage.RenameFile(srcVolume, srcFilePath, dstVolume, dstFilePath) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-19 07:19:29 +08:00
										 |  |  | // keepHTTPResponseAlive can be used to avoid timeouts with long storage
 | 
					
						
							| 
									
										
										
										
											2019-12-12 22:02:37 +08:00
										 |  |  | // operations, such as bitrot verification or data usage crawling.
 | 
					
						
							| 
									
										
										
										
											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.
 | 
					
						
							|  |  |  | // waitForHTTPResponse should be used to the receiving side.
 | 
					
						
							|  |  |  | func keepHTTPResponseAlive(w http.ResponseWriter) func() { | 
					
						
							| 
									
										
										
										
											2019-07-09 04:51:18 +08:00
										 |  |  | 	doneCh := make(chan struct{}) | 
					
						
							|  |  |  | 	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: | 
					
						
							|  |  |  | 				w.Write([]byte(" ")) | 
					
						
							|  |  |  | 				w.(http.Flusher).Flush() | 
					
						
							|  |  |  | 			case doneCh <- struct{}{}: | 
					
						
							| 
									
										
										
										
											2020-03-19 07:19:29 +08:00
										 |  |  | 				w.Write([]byte{0}) | 
					
						
							| 
									
										
										
										
											2019-07-09 04:51:18 +08:00
										 |  |  | 				ticker.Stop() | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							| 
									
										
										
										
											2020-03-19 07:19:29 +08:00
										 |  |  | 	return func() { | 
					
						
							|  |  |  | 		// Indicate we are ready to write.
 | 
					
						
							|  |  |  | 		<-doneCh | 
					
						
							|  |  |  | 		// Wait for channel to be closed so we don't race on writes.
 | 
					
						
							|  |  |  | 		<-doneCh | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // 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 | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if b != ' ' { | 
					
						
							|  |  |  | 			if b != 0 { | 
					
						
							|  |  |  | 				reader.UnreadByte() | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			break | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return reader, nil | 
					
						
							| 
									
										
										
										
											2019-07-09 04:51:18 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // VerifyFileResp - VerifyFile()'s response.
 | 
					
						
							|  |  |  | type VerifyFileResp struct { | 
					
						
							|  |  |  | 	Err error | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // VerifyFile - Verify the file for bitrot errors.
 | 
					
						
							|  |  |  | func (s *storageRESTServer) VerifyFile(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	if !s.IsValid(w, r) { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	vars := mux.Vars(r) | 
					
						
							|  |  |  | 	volume := vars[storageRESTVolume] | 
					
						
							|  |  |  | 	filePath := vars[storageRESTFilePath] | 
					
						
							| 
									
										
										
										
											2019-09-12 04:49:53 +08:00
										 |  |  | 	size, err := strconv.ParseInt(vars[storageRESTLength], 10, 0) | 
					
						
							| 
									
										
										
										
											2019-07-13 07:29:44 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-09-12 04:49:53 +08:00
										 |  |  | 	shardSize, err := strconv.Atoi(vars[storageRESTShardSize]) | 
					
						
							| 
									
										
										
										
											2019-07-09 04:51:18 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	hashStr := vars[storageRESTBitrotHash] | 
					
						
							|  |  |  | 	var hash []byte | 
					
						
							|  |  |  | 	if hashStr != "" { | 
					
						
							|  |  |  | 		hash, err = hex.DecodeString(hashStr) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	algoStr := vars[storageRESTBitrotAlgo] | 
					
						
							|  |  |  | 	if algoStr == "" { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, errInvalidArgument) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-07-12 04:19:25 +08:00
										 |  |  | 	w.Header().Set(xhttp.ContentType, "text/event-stream") | 
					
						
							| 
									
										
										
										
											2019-10-02 04:12:15 +08:00
										 |  |  | 	encoder := gob.NewEncoder(w) | 
					
						
							| 
									
										
										
										
											2020-03-19 07:19:29 +08:00
										 |  |  | 	done := keepHTTPResponseAlive(w) | 
					
						
							| 
									
										
										
										
											2019-10-02 04:12:15 +08:00
										 |  |  | 	err = s.storage.VerifyFile(volume, filePath, size, BitrotAlgorithmFromString(algoStr), hash, int64(shardSize)) | 
					
						
							| 
									
										
										
										
											2020-03-19 07:19:29 +08:00
										 |  |  | 	done() | 
					
						
							| 
									
										
										
										
											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
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | // registerStorageRPCRouter - register storage rpc router.
 | 
					
						
							| 
									
										
										
										
											2019-11-20 09:42:27 +08:00
										 |  |  | func registerStorageRESTHandlers(router *mux.Router, endpointZones EndpointZones) { | 
					
						
							|  |  |  | 	for _, ep := range endpointZones { | 
					
						
							|  |  |  | 		for _, endpoint := range ep.Endpoints { | 
					
						
							|  |  |  | 			if !endpoint.IsLocal { | 
					
						
							|  |  |  | 				continue | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			storage, err := newPosix(endpoint.Path) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2019-12-26 14:05:54 +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>" | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				hint := fmt.Sprintf("Run the following command to add the convenient permissions: `sudo chown %s %s && sudo chmod u+rxw %s`", | 
					
						
							|  |  |  | 					username, endpoint.Path, endpoint.Path) | 
					
						
							|  |  |  | 				logger.Fatal(config.ErrUnableToWriteInBackend(err).Hint(hint), | 
					
						
							| 
									
										
										
										
											2019-11-20 09:42:27 +08:00
										 |  |  | 					"Unable to initialize posix backend") | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-20 09:42:27 +08:00
										 |  |  | 			server := &storageRESTServer{storage: storage} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			subrouter := router.PathPrefix(path.Join(storageRESTPrefix, endpoint.Path)).Subrouter() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodDiskInfo).HandlerFunc(httpTraceHdrs(server.DiskInfoHandler)) | 
					
						
							| 
									
										
										
										
											2019-12-12 22:02:37 +08:00
										 |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodCrawlAndGetDataUsage).HandlerFunc(httpTraceHdrs(server.CrawlAndGetDataUsageHandler)) | 
					
						
							| 
									
										
										
										
											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)...) | 
					
						
							|  |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodCreateFile).HandlerFunc(httpTraceHdrs(server.CreateFileHandler)). | 
					
						
							|  |  |  | 				Queries(restQueries(storageRESTVolume, storageRESTFilePath, storageRESTLength)...) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodStatFile).HandlerFunc(httpTraceHdrs(server.StatFileHandler)). | 
					
						
							|  |  |  | 				Queries(restQueries(storageRESTVolume, storageRESTFilePath)...) | 
					
						
							|  |  |  | 			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)). | 
					
						
							|  |  |  | 				Queries(restQueries(storageRESTVolume, storageRESTDirPath, storageRESTCount, storageRESTLeafFile)...) | 
					
						
							|  |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodWalk).HandlerFunc(httpTraceHdrs(server.WalkHandler)). | 
					
						
							|  |  |  | 				Queries(restQueries(storageRESTVolume, storageRESTDirPath, storageRESTMarkerPath, storageRESTRecursive, storageRESTLeafFile)...) | 
					
						
							| 
									
										
										
										
											2020-03-23 10:23:47 +08:00
										 |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodWalkSplunk).HandlerFunc(httpTraceHdrs(server.WalkSplunkHandler)). | 
					
						
							|  |  |  | 				Queries(restQueries(storageRESTVolume, storageRESTDirPath, storageRESTMarkerPath)...) | 
					
						
							| 
									
										
										
										
											2020-03-11 23:56:36 +08:00
										 |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodDeletePrefixes).HandlerFunc(httpTraceHdrs(server.DeletePrefixesHandler)). | 
					
						
							|  |  |  | 				Queries(restQueries(storageRESTVolume)...) | 
					
						
							| 
									
										
										
										
											2019-11-20 09:42:27 +08:00
										 |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodDeleteFile).HandlerFunc(httpTraceHdrs(server.DeleteFileHandler)). | 
					
						
							|  |  |  | 				Queries(restQueries(storageRESTVolume, storageRESTFilePath)...) | 
					
						
							|  |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodDeleteFileBulk).HandlerFunc(httpTraceHdrs(server.DeleteFileBulkHandler)). | 
					
						
							| 
									
										
										
										
											2020-02-02 10:11:29 +08:00
										 |  |  | 				Queries(restQueries(storageRESTVolume)...) | 
					
						
							| 
									
										
										
										
											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)...) | 
					
						
							|  |  |  | 			subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodVerifyFile).HandlerFunc(httpTraceHdrs(server.VerifyFile)). | 
					
						
							|  |  |  | 				Queries(restQueries(storageRESTVolume, storageRESTFilePath, storageRESTBitrotAlgo, storageRESTBitrotHash, storageRESTLength, storageRESTShardSize)...) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | } |