| 
									
										
										
										
											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 ( | 
					
						
							| 
									
										
										
										
											2019-03-19 04:07:58 +08:00
										 |  |  | 	"encoding/gob" | 
					
						
							|  |  |  | 	"encoding/hex" | 
					
						
							| 
									
										
										
										
											2019-02-14 07:29:46 +08:00
										 |  |  | 	"errors" | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"io" | 
					
						
							| 
									
										
										
										
											2019-03-19 04:07:58 +08:00
										 |  |  | 	"net/http" | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	"path" | 
					
						
							|  |  |  | 	"strconv" | 
					
						
							|  |  |  | 	"time" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/gorilla/mux" | 
					
						
							|  |  |  | 	"github.com/minio/minio/cmd/logger" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-14 07:29:46 +08:00
										 |  |  | var errConnectionStale = errors.New("connection stale, REST client/server instance-id mismatch") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | // To abstract a disk over network.
 | 
					
						
							|  |  |  | type storageRESTServer struct { | 
					
						
							|  |  |  | 	storage *posix | 
					
						
							| 
									
										
										
										
											2019-02-14 07:29:46 +08:00
										 |  |  | 	// Used to detect reboot of servers so that peers revalidate format.json as
 | 
					
						
							|  |  |  | 	// different disk might be available on the same mount point after reboot.
 | 
					
						
							|  |  |  | 	instanceID string | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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-02-14 07:29:46 +08:00
										 |  |  | // Authenticates storage client's requests and validates for skewed time.
 | 
					
						
							|  |  |  | func storageServerRequestValidate(r *http.Request) error { | 
					
						
							| 
									
										
										
										
											2019-04-04 03:16:19 +08:00
										 |  |  | 	_, owner, err := webRequestAuthenticate(r) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2019-02-14 07:29:46 +08:00
										 |  |  | 		return err | 
					
						
							| 
									
										
										
										
											2019-02-13 05:24:14 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-04-04 03:16:19 +08:00
										 |  |  | 	if !owner { // Disable access for non-admin users.
 | 
					
						
							|  |  |  | 		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 { | 
					
						
							|  |  |  | 		delta = delta * -1 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	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) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	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 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	instanceID := r.URL.Query().Get(storageRESTInstanceID) | 
					
						
							|  |  |  | 	if instanceID != s.instanceID { | 
					
						
							|  |  |  | 		// This will cause the peer to revalidate format.json using a new storage-rest-client instance.
 | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, errConnectionStale) | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 		return false | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return true | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-14 07:29:46 +08:00
										 |  |  | // GetInstanceID - returns the instance ID of the server.
 | 
					
						
							|  |  |  | func (s *storageRESTServer) GetInstanceID(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	if err := storageServerRequestValidate(r); err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	w.Header().Set("Content-Length", strconv.Itoa(len(s.instanceID))) | 
					
						
							|  |  |  | 	w.Write([]byte(s.instanceID)) | 
					
						
							| 
									
										
										
										
											2019-03-18 13:20:26 +08:00
										 |  |  | 	w.(http.Flusher).Flush() | 
					
						
							| 
									
										
										
										
											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) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // 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) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // 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 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	defer w.(http.Flusher).Flush() | 
					
						
							|  |  |  | 	gob.NewEncoder(w).Encode(&infos) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // 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 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	defer w.(http.Flusher).Flush() | 
					
						
							|  |  |  | 	gob.NewEncoder(w).Encode(info) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // 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] | 
					
						
							|  |  |  | 	err := s.storage.DeleteVol(volume) | 
					
						
							|  |  |  | 	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 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	buf := make([]byte, r.ContentLength) | 
					
						
							|  |  |  | 	_, err := io.ReadFull(r.Body, buf) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	err = s.storage.WriteAll(volume, filePath, buf) | 
					
						
							|  |  |  | 	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 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	defer w.(http.Flusher).Flush() | 
					
						
							|  |  |  | 	gob.NewEncoder(w).Encode(info) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // 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 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	w.Header().Set("Content-Length", strconv.Itoa(len(buf))) | 
					
						
							|  |  |  | 	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 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	w.Header().Set("Content-Length", strconv.Itoa(len(buf))) | 
					
						
							|  |  |  | 	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() | 
					
						
							|  |  |  | 	w.Header().Set("Content-Length", 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-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 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	entries, err := s.storage.ListDir(volume, dirPath, count) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	defer w.(http.Flusher).Flush() | 
					
						
							|  |  |  | 	gob.NewEncoder(w).Encode(&entries) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // DeleteFileHandler - delete a file.
 | 
					
						
							|  |  |  | func (s *storageRESTServer) DeleteFileHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	if !s.IsValid(w, r) { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	vars := mux.Vars(r) | 
					
						
							|  |  |  | 	volume := vars[storageRESTVolume] | 
					
						
							|  |  |  | 	filePath := vars[storageRESTFilePath] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	err := s.storage.DeleteFile(volume, filePath) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		s.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // 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) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // registerStorageRPCRouter - register storage rpc router.
 | 
					
						
							|  |  |  | func registerStorageRESTHandlers(router *mux.Router, endpoints EndpointList) { | 
					
						
							|  |  |  | 	for _, endpoint := range endpoints { | 
					
						
							|  |  |  | 		if !endpoint.IsLocal { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		storage, err := newPosix(endpoint.Path) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			logger.Fatal(uiErrUnableToWriteInBackend(err), "Unable to initialize posix backend") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-14 07:29:46 +08:00
										 |  |  | 		server := &storageRESTServer{storage, mustGetUUID()} | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		subrouter := router.PathPrefix(path.Join(storageRESTPath, endpoint.Path)).Subrouter() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		subrouter.Methods(http.MethodPost).Path("/" + storageRESTMethodDiskInfo).HandlerFunc(httpTraceHdrs(server.DiskInfoHandler)) | 
					
						
							|  |  |  | 		subrouter.Methods(http.MethodPost).Path("/" + storageRESTMethodMakeVol).HandlerFunc(httpTraceHdrs(server.MakeVolHandler)).Queries(restQueries(storageRESTVolume)...) | 
					
						
							|  |  |  | 		subrouter.Methods(http.MethodPost).Path("/" + storageRESTMethodStatVol).HandlerFunc(httpTraceHdrs(server.StatVolHandler)).Queries(restQueries(storageRESTVolume)...) | 
					
						
							|  |  |  | 		subrouter.Methods(http.MethodPost).Path("/" + storageRESTMethodDeleteVol).HandlerFunc(httpTraceHdrs(server.DeleteVolHandler)).Queries(restQueries(storageRESTVolume)...) | 
					
						
							|  |  |  | 		subrouter.Methods(http.MethodPost).Path("/" + storageRESTMethodListVols).HandlerFunc(httpTraceHdrs(server.ListVolsHandler)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		subrouter.Methods(http.MethodPost).Path("/" + storageRESTMethodAppendFile).HandlerFunc(httpTraceHdrs(server.AppendFileHandler)). | 
					
						
							|  |  |  | 			Queries(restQueries(storageRESTVolume, storageRESTFilePath)...) | 
					
						
							| 
									
										
										
										
											2018-11-14 22:18:35 +08:00
										 |  |  | 		subrouter.Methods(http.MethodPost).Path("/" + storageRESTMethodWriteAll).HandlerFunc(httpTraceHdrs(server.WriteAllHandler)). | 
					
						
							|  |  |  | 			Queries(restQueries(storageRESTVolume, storageRESTFilePath)...) | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | 		subrouter.Methods(http.MethodPost).Path("/" + storageRESTMethodCreateFile).HandlerFunc(httpTraceHdrs(server.CreateFileHandler)). | 
					
						
							|  |  |  | 			Queries(restQueries(storageRESTVolume, storageRESTFilePath, storageRESTLength)...) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 		subrouter.Methods(http.MethodPost).Path("/" + storageRESTMethodStatFile).HandlerFunc(httpTraceHdrs(server.StatFileHandler)). | 
					
						
							|  |  |  | 			Queries(restQueries(storageRESTVolume, storageRESTFilePath)...) | 
					
						
							|  |  |  | 		subrouter.Methods(http.MethodPost).Path("/" + storageRESTMethodReadAll).HandlerFunc(httpTraceHdrs(server.ReadAllHandler)). | 
					
						
							|  |  |  | 			Queries(restQueries(storageRESTVolume, storageRESTFilePath)...) | 
					
						
							|  |  |  | 		subrouter.Methods(http.MethodPost).Path("/" + storageRESTMethodReadFile).HandlerFunc(httpTraceHdrs(server.ReadFileHandler)). | 
					
						
							|  |  |  | 			Queries(restQueries(storageRESTVolume, storageRESTFilePath, storageRESTOffset, storageRESTLength, storageRESTBitrotAlgo, storageRESTBitrotHash)...) | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | 		subrouter.Methods(http.MethodPost).Path("/" + storageRESTMethodReadFileStream).HandlerFunc(httpTraceHdrs(server.ReadFileStreamHandler)). | 
					
						
							|  |  |  | 			Queries(restQueries(storageRESTVolume, storageRESTFilePath, storageRESTOffset, storageRESTLength)...) | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 		subrouter.Methods(http.MethodPost).Path("/" + storageRESTMethodListDir).HandlerFunc(httpTraceHdrs(server.ListDirHandler)). | 
					
						
							|  |  |  | 			Queries(restQueries(storageRESTVolume, storageRESTDirPath, storageRESTCount)...) | 
					
						
							|  |  |  | 		subrouter.Methods(http.MethodPost).Path("/" + storageRESTMethodDeleteFile).HandlerFunc(httpTraceHdrs(server.DeleteFileHandler)). | 
					
						
							|  |  |  | 			Queries(restQueries(storageRESTVolume, storageRESTFilePath)...) | 
					
						
							|  |  |  | 		subrouter.Methods(http.MethodPost).Path("/" + storageRESTMethodRenameFile).HandlerFunc(httpTraceHdrs(server.RenameFileHandler)). | 
					
						
							|  |  |  | 			Queries(restQueries(storageRESTSrcVolume, storageRESTSrcPath, storageRESTDstVolume, storageRESTDstPath)...) | 
					
						
							| 
									
										
										
										
											2019-02-14 07:29:46 +08:00
										 |  |  | 		subrouter.Methods(http.MethodPost).Path("/" + storageRESTMethodGetInstanceID).HandlerFunc(httpTraceAll(server.GetInstanceID)) | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-12-06 03:54:12 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	router.NotFoundHandler = http.HandlerFunc(httpTraceAll(notFoundHandler)) | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | } |