| 
									
										
										
										
											2021-04-19 03:41:13 +08:00
										 |  |  | // Copyright (c) 2015-2021 MinIO, Inc.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This file is part of MinIO Object Storage stack
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This program is free software: you can redistribute it and/or modify
 | 
					
						
							|  |  |  | // it under the terms of the GNU Affero General Public License as published by
 | 
					
						
							|  |  |  | // the Free Software Foundation, either version 3 of the License, or
 | 
					
						
							|  |  |  | // (at your option) any later version.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This program is distributed in the hope that it will be useful
 | 
					
						
							|  |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					
						
							|  |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					
						
							|  |  |  | // GNU Affero General Public License for more details.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // You should have received a copy of the GNU Affero General Public License
 | 
					
						
							|  |  |  | // along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					
						
							| 
									
										
										
										
											2019-04-18 14:16:27 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | package cmd | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2020-01-04 01:41:07 +08:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2019-04-18 14:16:27 +08:00
										 |  |  | 	"errors" | 
					
						
							| 
									
										
										
										
											2021-10-01 02:53:01 +08:00
										 |  |  | 	"io" | 
					
						
							| 
									
										
										
										
											2019-04-18 14:16:27 +08:00
										 |  |  | 	"net/http" | 
					
						
							|  |  |  | 	"time" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-01 02:53:01 +08:00
										 |  |  | 	"github.com/dustin/go-humanize" | 
					
						
							| 
									
										
										
										
											2021-06-02 05:59:40 +08:00
										 |  |  | 	"github.com/minio/minio/internal/dsync" | 
					
						
							| 
									
										
										
										
											2023-01-23 19:12:47 +08:00
										 |  |  | 	"github.com/minio/mux" | 
					
						
							| 
									
										
										
										
											2019-04-18 14:16:27 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const ( | 
					
						
							|  |  |  | 	// Lock maintenance interval.
 | 
					
						
							| 
									
										
										
										
											2021-02-23 08:29:53 +08:00
										 |  |  | 	lockMaintenanceInterval = 1 * time.Minute | 
					
						
							| 
									
										
										
										
											2019-04-18 14:16:27 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-04 10:36:43 +08:00
										 |  |  | 	// Lock validity duration
 | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | 	lockValidityDuration = 1 * time.Minute | 
					
						
							| 
									
										
										
										
											2019-04-18 14:16:27 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // To abstract a node over network.
 | 
					
						
							|  |  |  | type lockRESTServer struct { | 
					
						
							| 
									
										
										
										
											2019-08-29 07:12:57 +08:00
										 |  |  | 	ll *localLocker | 
					
						
							| 
									
										
										
										
											2019-04-18 14:16:27 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (l *lockRESTServer) writeErrorResponse(w http.ResponseWriter, err error) { | 
					
						
							| 
									
										
										
										
											2022-10-27 00:51:26 +08:00
										 |  |  | 	statusCode := http.StatusForbidden | 
					
						
							|  |  |  | 	switch err { | 
					
						
							|  |  |  | 	case errLockNotInitialized: | 
					
						
							|  |  |  | 		// Return 425 instead of 5xx, otherwise this node will be marked offline
 | 
					
						
							|  |  |  | 		statusCode = http.StatusTooEarly | 
					
						
							|  |  |  | 	case errLockConflict: | 
					
						
							|  |  |  | 		statusCode = http.StatusConflict | 
					
						
							|  |  |  | 	case errLockNotFound: | 
					
						
							|  |  |  | 		statusCode = http.StatusNotFound | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	w.WriteHeader(statusCode) | 
					
						
							| 
									
										
										
										
											2019-04-18 14:16:27 +08:00
										 |  |  | 	w.Write([]byte(err.Error())) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // IsValid - To authenticate and verify the time difference.
 | 
					
						
							|  |  |  | func (l *lockRESTServer) IsValid(w http.ResponseWriter, r *http.Request) bool { | 
					
						
							| 
									
										
										
										
											2020-09-24 03:00:29 +08:00
										 |  |  | 	if l.ll == nil { | 
					
						
							|  |  |  | 		l.writeErrorResponse(w, errLockNotInitialized) | 
					
						
							|  |  |  | 		return false | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-18 14:16:27 +08:00
										 |  |  | 	if err := storageServerRequestValidate(r); err != nil { | 
					
						
							|  |  |  | 		l.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return false | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return true | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-21 13:59:57 +08:00
										 |  |  | func getLockArgs(r *http.Request) (args dsync.LockArgs, err error) { | 
					
						
							| 
									
										
										
										
											2021-10-01 02:53:01 +08:00
										 |  |  | 	dec := msgpNewReader(io.LimitReader(r.Body, 1000*humanize.KiByte)) | 
					
						
							| 
									
										
										
										
											2023-07-07 07:02:08 +08:00
										 |  |  | 	defer readMsgpReaderPoolPut(dec) | 
					
						
							| 
									
										
										
										
											2021-10-01 02:53:01 +08:00
										 |  |  | 	err = args.DecodeMsg(dec) | 
					
						
							|  |  |  | 	return args, err | 
					
						
							| 
									
										
										
										
											2019-08-06 02:45:30 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-18 05:49:26 +08:00
										 |  |  | // HealthHandler returns success if request is authenticated.
 | 
					
						
							|  |  |  | func (l *lockRESTServer) HealthHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	l.IsValid(w, r) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-04 10:36:43 +08:00
										 |  |  | // RefreshHandler - refresh the current lock
 | 
					
						
							|  |  |  | func (l *lockRESTServer) RefreshHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	if !l.IsValid(w, r) { | 
					
						
							|  |  |  | 		l.writeErrorResponse(w, errors.New("invalid request")) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	args, err := getLockArgs(r) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		l.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	refreshed, err := l.ll.Refresh(r.Context(), args) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		l.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if !refreshed { | 
					
						
							|  |  |  | 		l.writeErrorResponse(w, errLockNotFound) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-18 14:16:27 +08:00
										 |  |  | // LockHandler - Acquires a lock.
 | 
					
						
							|  |  |  | func (l *lockRESTServer) LockHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	if !l.IsValid(w, r) { | 
					
						
							| 
									
										
										
										
											2021-01-26 02:01:27 +08:00
										 |  |  | 		l.writeErrorResponse(w, errors.New("invalid request")) | 
					
						
							| 
									
										
										
										
											2019-04-18 14:16:27 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-21 13:59:57 +08:00
										 |  |  | 	args, err := getLockArgs(r) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		l.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-30 14:15:34 +08:00
										 |  |  | 	success, err := l.ll.Lock(r.Context(), args) | 
					
						
							| 
									
										
										
										
											2019-08-29 07:12:57 +08:00
										 |  |  | 	if err == nil && !success { | 
					
						
							|  |  |  | 		err = errLockConflict | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2019-04-18 14:16:27 +08:00
										 |  |  | 		l.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // UnlockHandler - releases the acquired lock.
 | 
					
						
							|  |  |  | func (l *lockRESTServer) UnlockHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	if !l.IsValid(w, r) { | 
					
						
							| 
									
										
										
										
											2021-01-26 02:01:27 +08:00
										 |  |  | 		l.writeErrorResponse(w, errors.New("invalid request")) | 
					
						
							| 
									
										
										
										
											2019-04-18 14:16:27 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-21 13:59:57 +08:00
										 |  |  | 	args, err := getLockArgs(r) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		l.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-11 17:11:29 +08:00
										 |  |  | 	_, err = l.ll.Unlock(context.Background(), args) | 
					
						
							| 
									
										
										
										
											2019-08-29 07:12:57 +08:00
										 |  |  | 	// Ignore the Unlock() "reply" return value because if err == nil, "reply" is always true
 | 
					
						
							|  |  |  | 	// Consequently, if err != nil, reply is always false
 | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2019-04-18 14:16:27 +08:00
										 |  |  | 		l.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // LockHandler - Acquires an RLock.
 | 
					
						
							|  |  |  | func (l *lockRESTServer) RLockHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	if !l.IsValid(w, r) { | 
					
						
							| 
									
										
										
										
											2021-01-26 02:01:27 +08:00
										 |  |  | 		l.writeErrorResponse(w, errors.New("invalid request")) | 
					
						
							| 
									
										
										
										
											2019-04-18 14:16:27 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-21 13:59:57 +08:00
										 |  |  | 	args, err := getLockArgs(r) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		l.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-30 14:15:34 +08:00
										 |  |  | 	success, err := l.ll.RLock(r.Context(), args) | 
					
						
							| 
									
										
										
										
											2019-08-29 07:12:57 +08:00
										 |  |  | 	if err == nil && !success { | 
					
						
							|  |  |  | 		err = errLockConflict | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2019-04-18 14:16:27 +08:00
										 |  |  | 		l.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // RUnlockHandler - releases the acquired read lock.
 | 
					
						
							|  |  |  | func (l *lockRESTServer) RUnlockHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	if !l.IsValid(w, r) { | 
					
						
							| 
									
										
										
										
											2021-01-26 02:01:27 +08:00
										 |  |  | 		l.writeErrorResponse(w, errors.New("invalid request")) | 
					
						
							| 
									
										
										
										
											2019-04-18 14:16:27 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-21 13:59:57 +08:00
										 |  |  | 	args, err := getLockArgs(r) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		l.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-29 07:12:57 +08:00
										 |  |  | 	// Ignore the RUnlock() "reply" return value because if err == nil, "reply" is always true.
 | 
					
						
							|  |  |  | 	// Consequently, if err != nil, reply is always false
 | 
					
						
							| 
									
										
										
										
											2021-05-11 17:11:29 +08:00
										 |  |  | 	if _, err = l.ll.RUnlock(context.Background(), args); err != nil { | 
					
						
							| 
									
										
										
										
											2019-04-18 14:16:27 +08:00
										 |  |  | 		l.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-26 02:01:27 +08:00
										 |  |  | // ForceUnlockHandler - query expired lock status.
 | 
					
						
							|  |  |  | func (l *lockRESTServer) ForceUnlockHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	if !l.IsValid(w, r) { | 
					
						
							|  |  |  | 		l.writeErrorResponse(w, errors.New("invalid request")) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	args, err := getLockArgs(r) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		l.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if _, err = l.ll.ForceUnlock(r.Context(), args); err != nil { | 
					
						
							|  |  |  | 		l.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-04 10:36:43 +08:00
										 |  |  | // lockMaintenance loops over all locks and discards locks
 | 
					
						
							|  |  |  | // that have not been refreshed for some time.
 | 
					
						
							|  |  |  | func lockMaintenance(ctx context.Context) { | 
					
						
							| 
									
										
										
										
											2022-05-31 01:58:37 +08:00
										 |  |  | 	if !globalIsDistErasure { | 
					
						
							| 
									
										
										
										
											2021-03-04 10:36:43 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-23 08:29:53 +08:00
										 |  |  | 	// Initialize a new ticker with 1 minute between each ticks.
 | 
					
						
							| 
									
										
										
										
											2021-02-06 11:23:48 +08:00
										 |  |  | 	lkTimer := time.NewTimer(lockMaintenanceInterval) | 
					
						
							|  |  |  | 	// Stop the timer upon returning.
 | 
					
						
							|  |  |  | 	defer lkTimer.Stop() | 
					
						
							| 
									
										
										
										
											2019-11-26 08:39:43 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	for { | 
					
						
							|  |  |  | 		// Verifies every minute for locks held more than 2 minutes.
 | 
					
						
							|  |  |  | 		select { | 
					
						
							| 
									
										
										
										
											2020-03-23 03:16:36 +08:00
										 |  |  | 		case <-ctx.Done(): | 
					
						
							| 
									
										
										
										
											2019-11-26 08:39:43 +08:00
										 |  |  | 			return | 
					
						
							| 
									
										
										
										
											2021-02-06 11:23:48 +08:00
										 |  |  | 		case <-lkTimer.C: | 
					
						
							| 
									
										
										
										
											2022-05-18 13:42:59 +08:00
										 |  |  | 			globalLockServer.expireOldLocks(lockValidityDuration) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-06 11:23:48 +08:00
										 |  |  | 			// Reset the timer for next cycle.
 | 
					
						
							| 
									
										
										
										
											2021-03-04 10:36:43 +08:00
										 |  |  | 			lkTimer.Reset(lockMaintenanceInterval) | 
					
						
							| 
									
										
										
										
											2019-11-26 08:39:43 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-14 04:17:45 +08:00
										 |  |  | // registerLockRESTHandlers - register lock rest router.
 | 
					
						
							| 
									
										
										
										
											2020-12-10 23:28:37 +08:00
										 |  |  | func registerLockRESTHandlers(router *mux.Router) { | 
					
						
							|  |  |  | 	lockServer := &lockRESTServer{ | 
					
						
							|  |  |  | 		ll: newLocker(), | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-11-20 09:42:27 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-10 23:28:37 +08:00
										 |  |  | 	subrouter := router.PathPrefix(lockRESTPrefix).Subrouter() | 
					
						
							|  |  |  | 	subrouter.Methods(http.MethodPost).Path(lockRESTVersionPrefix + lockRESTMethodHealth).HandlerFunc(httpTraceHdrs(lockServer.HealthHandler)) | 
					
						
							| 
									
										
										
										
											2021-03-04 10:36:43 +08:00
										 |  |  | 	subrouter.Methods(http.MethodPost).Path(lockRESTVersionPrefix + lockRESTMethodRefresh).HandlerFunc(httpTraceHdrs(lockServer.RefreshHandler)) | 
					
						
							| 
									
										
										
										
											2020-12-10 23:28:37 +08:00
										 |  |  | 	subrouter.Methods(http.MethodPost).Path(lockRESTVersionPrefix + lockRESTMethodLock).HandlerFunc(httpTraceHdrs(lockServer.LockHandler)) | 
					
						
							|  |  |  | 	subrouter.Methods(http.MethodPost).Path(lockRESTVersionPrefix + lockRESTMethodRLock).HandlerFunc(httpTraceHdrs(lockServer.RLockHandler)) | 
					
						
							|  |  |  | 	subrouter.Methods(http.MethodPost).Path(lockRESTVersionPrefix + lockRESTMethodUnlock).HandlerFunc(httpTraceHdrs(lockServer.UnlockHandler)) | 
					
						
							|  |  |  | 	subrouter.Methods(http.MethodPost).Path(lockRESTVersionPrefix + lockRESTMethodRUnlock).HandlerFunc(httpTraceHdrs(lockServer.RUnlockHandler)) | 
					
						
							| 
									
										
										
										
											2021-01-26 02:01:27 +08:00
										 |  |  | 	subrouter.Methods(http.MethodPost).Path(lockRESTVersionPrefix + lockRESTMethodForceUnlock).HandlerFunc(httpTraceAll(lockServer.ForceUnlockHandler)) | 
					
						
							| 
									
										
										
										
											2019-11-20 09:42:27 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-10 23:28:37 +08:00
										 |  |  | 	globalLockServer = lockServer.ll | 
					
						
							| 
									
										
										
										
											2019-11-26 08:39:43 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-04 10:36:43 +08:00
										 |  |  | 	go lockMaintenance(GlobalContext) | 
					
						
							| 
									
										
										
										
											2019-04-18 14:16:27 +08:00
										 |  |  | } |