| 
									
										
										
										
											2016-08-01 05:11:14 +08:00
										 |  |  | /* | 
					
						
							|  |  |  |  * Minio Cloud Storage, (C) 2016 Minio, Inc. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * 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. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-19 05:50:50 +08:00
										 |  |  | package cmd | 
					
						
							| 
									
										
										
										
											2016-08-01 05:11:14 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2016-08-23 02:01:21 +08:00
										 |  |  | 	"errors" | 
					
						
							| 
									
										
										
										
											2016-08-01 05:11:14 +08:00
										 |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"net/rpc" | 
					
						
							|  |  |  | 	"path" | 
					
						
							|  |  |  | 	"strings" | 
					
						
							|  |  |  | 	"sync" | 
					
						
							| 
									
										
										
										
											2016-08-23 02:01:21 +08:00
										 |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2016-08-01 05:11:14 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	router "github.com/gorilla/mux" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-10 14:57:16 +08:00
										 |  |  | const lockRPCPath = "/minio/lock" | 
					
						
							| 
									
										
										
										
											2016-08-01 05:11:14 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-23 02:01:21 +08:00
										 |  |  | // LockArgs besides lock name, holds Token and Timestamp for session
 | 
					
						
							|  |  |  | // authentication and validation server restart.
 | 
					
						
							|  |  |  | type LockArgs struct { | 
					
						
							|  |  |  | 	Name      string | 
					
						
							|  |  |  | 	Token     string | 
					
						
							|  |  |  | 	Timestamp time.Time | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // SetToken - sets the token to the supplied value.
 | 
					
						
							|  |  |  | func (l *LockArgs) SetToken(token string) { | 
					
						
							|  |  |  | 	l.Token = token | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // SetTimestamp - sets the timestamp to the supplied value.
 | 
					
						
							|  |  |  | func (l *LockArgs) SetTimestamp(tstamp time.Time) { | 
					
						
							|  |  |  | 	l.Timestamp = tstamp | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-01 05:11:14 +08:00
										 |  |  | type lockServer struct { | 
					
						
							|  |  |  | 	rpcPath string | 
					
						
							|  |  |  | 	mutex   sync.Mutex | 
					
						
							| 
									
										
										
										
											2016-08-15 07:57:01 +08:00
										 |  |  | 	// e.g, when a Lock(name) is held, map[string][]bool{"name" : []bool{true}}
 | 
					
						
							|  |  |  | 	// when one or more RLock() is held, map[string][]bool{"name" : []bool{false, false}}
 | 
					
						
							| 
									
										
										
										
											2016-08-23 02:01:21 +08:00
										 |  |  | 	lockMap   map[string][]bool | 
					
						
							|  |  |  | 	timestamp time.Time // Timestamp set at the time of initialization. Resets naturally on minio server restart.
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-24 10:19:24 +08:00
										 |  |  | func (l *lockServer) verifyArgs(args *LockArgs) error { | 
					
						
							|  |  |  | 	if !l.timestamp.Equal(args.Timestamp) { | 
					
						
							|  |  |  | 		return errors.New("Timestamps don't match, server may have restarted.") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if !isRPCTokenValid(args.Token) { | 
					
						
							|  |  |  | 		return errors.New("Invalid token") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							| 
									
										
										
										
											2016-08-01 05:11:14 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ///  Distributed lock handlers
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-23 02:01:21 +08:00
										 |  |  | // LoginHandler - handles LoginHandler RPC call.
 | 
					
						
							|  |  |  | func (l *lockServer) LoginHandler(args *RPCLoginArgs, reply *RPCLoginReply) error { | 
					
						
							|  |  |  | 	jwt, err := newJWT(defaultTokenExpiry) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if err = jwt.Authenticate(args.Username, args.Password); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	token, err := jwt.GenerateToken(args.Username) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	reply.Token = token | 
					
						
							|  |  |  | 	reply.Timestamp = l.timestamp | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-01 05:11:14 +08:00
										 |  |  | // LockHandler - rpc handler for lock operation.
 | 
					
						
							| 
									
										
										
										
											2016-08-23 02:01:21 +08:00
										 |  |  | func (l *lockServer) Lock(args *LockArgs, reply *bool) error { | 
					
						
							| 
									
										
										
										
											2016-08-01 05:11:14 +08:00
										 |  |  | 	l.mutex.Lock() | 
					
						
							|  |  |  | 	defer l.mutex.Unlock() | 
					
						
							| 
									
										
										
										
											2016-08-24 10:19:24 +08:00
										 |  |  | 	if err := l.verifyArgs(args); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							| 
									
										
										
										
											2016-08-23 02:01:21 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	_, ok := l.lockMap[args.Name] | 
					
						
							| 
									
										
										
										
											2016-08-18 03:36:22 +08:00
										 |  |  | 	// No locks held on the given name.
 | 
					
						
							| 
									
										
										
										
											2016-08-01 05:11:14 +08:00
										 |  |  | 	if !ok { | 
					
						
							|  |  |  | 		*reply = true | 
					
						
							| 
									
										
										
										
											2016-08-23 02:01:21 +08:00
										 |  |  | 		l.lockMap[args.Name] = []bool{true} | 
					
						
							| 
									
										
										
										
											2016-08-01 05:11:14 +08:00
										 |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-08-18 03:36:22 +08:00
										 |  |  | 	// Either a read or write lock is held on the given name.
 | 
					
						
							| 
									
										
										
										
											2016-08-01 05:11:14 +08:00
										 |  |  | 	*reply = false | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // UnlockHandler - rpc handler for unlock operation.
 | 
					
						
							| 
									
										
										
										
											2016-08-23 02:01:21 +08:00
										 |  |  | func (l *lockServer) Unlock(args *LockArgs, reply *bool) error { | 
					
						
							| 
									
										
										
										
											2016-08-01 05:11:14 +08:00
										 |  |  | 	l.mutex.Lock() | 
					
						
							|  |  |  | 	defer l.mutex.Unlock() | 
					
						
							| 
									
										
										
										
											2016-08-24 10:19:24 +08:00
										 |  |  | 	if err := l.verifyArgs(args); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							| 
									
										
										
										
											2016-08-23 02:01:21 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	_, ok := l.lockMap[args.Name] | 
					
						
							| 
									
										
										
										
											2016-08-18 03:36:22 +08:00
										 |  |  | 	// No lock is held on the given name, there must be some issue at the lock client side.
 | 
					
						
							| 
									
										
										
										
											2016-08-01 05:11:14 +08:00
										 |  |  | 	if !ok { | 
					
						
							| 
									
										
										
										
											2016-08-23 02:01:21 +08:00
										 |  |  | 		return fmt.Errorf("Unlock attempted on an un-locked entity: %s", args.Name) | 
					
						
							| 
									
										
										
										
											2016-08-01 05:11:14 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	*reply = true | 
					
						
							| 
									
										
										
										
											2016-08-23 02:01:21 +08:00
										 |  |  | 	delete(l.lockMap, args.Name) | 
					
						
							| 
									
										
										
										
											2016-08-01 05:11:14 +08:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-23 02:01:21 +08:00
										 |  |  | func (l *lockServer) RLock(args *LockArgs, reply *bool) error { | 
					
						
							| 
									
										
										
										
											2016-08-15 07:57:01 +08:00
										 |  |  | 	l.mutex.Lock() | 
					
						
							|  |  |  | 	defer l.mutex.Unlock() | 
					
						
							| 
									
										
										
										
											2016-08-24 10:19:24 +08:00
										 |  |  | 	if err := l.verifyArgs(args); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							| 
									
										
										
										
											2016-08-23 02:01:21 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	locksHeld, ok := l.lockMap[args.Name] | 
					
						
							| 
									
										
										
										
											2016-08-18 03:36:22 +08:00
										 |  |  | 	// No locks held on the given name.
 | 
					
						
							| 
									
										
										
										
											2016-08-15 07:57:01 +08:00
										 |  |  | 	if !ok { | 
					
						
							|  |  |  | 		// First read-lock to be held on *name.
 | 
					
						
							| 
									
										
										
										
											2016-08-23 02:01:21 +08:00
										 |  |  | 		l.lockMap[args.Name] = []bool{false} | 
					
						
							| 
									
										
										
										
											2016-08-18 03:36:22 +08:00
										 |  |  | 		*reply = true | 
					
						
							|  |  |  | 	} else if len(locksHeld) == 1 && locksHeld[0] == true { | 
					
						
							|  |  |  | 		// A write-lock is held, read lock can't be granted.
 | 
					
						
							|  |  |  | 		*reply = false | 
					
						
							| 
									
										
										
										
											2016-08-15 07:57:01 +08:00
										 |  |  | 	} else { | 
					
						
							|  |  |  | 		// Add an entry for this read lock.
 | 
					
						
							| 
									
										
										
										
											2016-08-23 02:01:21 +08:00
										 |  |  | 		l.lockMap[args.Name] = append(locksHeld, false) | 
					
						
							| 
									
										
										
										
											2016-08-18 03:36:22 +08:00
										 |  |  | 		*reply = true | 
					
						
							| 
									
										
										
										
											2016-08-15 07:57:01 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-23 02:01:21 +08:00
										 |  |  | func (l *lockServer) RUnlock(args *LockArgs, reply *bool) error { | 
					
						
							| 
									
										
										
										
											2016-08-15 07:57:01 +08:00
										 |  |  | 	l.mutex.Lock() | 
					
						
							|  |  |  | 	defer l.mutex.Unlock() | 
					
						
							| 
									
										
										
										
											2016-08-24 10:19:24 +08:00
										 |  |  | 	if err := l.verifyArgs(args); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							| 
									
										
										
										
											2016-08-23 02:01:21 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	locksHeld, ok := l.lockMap[args.Name] | 
					
						
							| 
									
										
										
										
											2016-08-15 07:57:01 +08:00
										 |  |  | 	if !ok { | 
					
						
							| 
									
										
										
										
											2016-08-23 02:01:21 +08:00
										 |  |  | 		return fmt.Errorf("RUnlock attempted on an un-locked entity: %s", args.Name) | 
					
						
							| 
									
										
										
										
											2016-08-15 07:57:01 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	if len(locksHeld) > 1 { | 
					
						
							|  |  |  | 		// Remove one of the read locks held.
 | 
					
						
							|  |  |  | 		locksHeld = locksHeld[1:] | 
					
						
							| 
									
										
										
										
											2016-08-23 02:01:21 +08:00
										 |  |  | 		l.lockMap[args.Name] = locksHeld | 
					
						
							| 
									
										
										
										
											2016-08-18 03:36:22 +08:00
										 |  |  | 		*reply = true | 
					
						
							| 
									
										
										
										
											2016-08-15 07:57:01 +08:00
										 |  |  | 	} else { | 
					
						
							|  |  |  | 		// Delete the map entry since this is the last read lock held
 | 
					
						
							|  |  |  | 		// on *name.
 | 
					
						
							| 
									
										
										
										
											2016-08-23 02:01:21 +08:00
										 |  |  | 		delete(l.lockMap, args.Name) | 
					
						
							| 
									
										
										
										
											2016-08-18 03:36:22 +08:00
										 |  |  | 		*reply = true | 
					
						
							| 
									
										
										
										
											2016-08-15 07:57:01 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-01 05:11:14 +08:00
										 |  |  | // Initialize distributed lock.
 | 
					
						
							|  |  |  | func initDistributedNSLock(mux *router.Router, serverConfig serverCmdConfig) { | 
					
						
							|  |  |  | 	lockServers := newLockServers(serverConfig) | 
					
						
							|  |  |  | 	registerStorageLockers(mux, lockServers) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Create one lock server for every local storage rpc server.
 | 
					
						
							|  |  |  | func newLockServers(serverConfig serverCmdConfig) (lockServers []*lockServer) { | 
					
						
							|  |  |  | 	// Initialize posix storage API.
 | 
					
						
							|  |  |  | 	exports := serverConfig.disks | 
					
						
							|  |  |  | 	ignoredExports := serverConfig.ignoredDisks | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Save ignored disks in a map
 | 
					
						
							|  |  |  | 	skipDisks := make(map[string]bool) | 
					
						
							|  |  |  | 	for _, ignoredExport := range ignoredExports { | 
					
						
							|  |  |  | 		skipDisks[ignoredExport] = true | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	for _, export := range exports { | 
					
						
							|  |  |  | 		if skipDisks[export] { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-08-10 14:57:16 +08:00
										 |  |  | 		if isLocalStorage(export) { | 
					
						
							|  |  |  | 			if idx := strings.LastIndex(export, ":"); idx != -1 { | 
					
						
							|  |  |  | 				export = export[idx+1:] | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			lockServers = append(lockServers, &lockServer{ | 
					
						
							| 
									
										
										
										
											2016-08-23 02:01:21 +08:00
										 |  |  | 				rpcPath:   export, | 
					
						
							|  |  |  | 				mutex:     sync.Mutex{}, | 
					
						
							|  |  |  | 				lockMap:   make(map[string][]bool), | 
					
						
							|  |  |  | 				timestamp: time.Now().UTC(), | 
					
						
							| 
									
										
										
										
											2016-08-10 14:57:16 +08:00
										 |  |  | 			}) | 
					
						
							| 
									
										
										
										
											2016-08-01 05:11:14 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return lockServers | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-10 14:57:16 +08:00
										 |  |  | // registerStorageLockers - register locker rpc handlers for net/rpc library clients
 | 
					
						
							| 
									
										
										
										
											2016-08-01 05:11:14 +08:00
										 |  |  | func registerStorageLockers(mux *router.Router, lockServers []*lockServer) { | 
					
						
							|  |  |  | 	for _, lockServer := range lockServers { | 
					
						
							| 
									
										
										
										
											2016-08-10 14:57:16 +08:00
										 |  |  | 		lockRPCServer := rpc.NewServer() | 
					
						
							| 
									
										
										
										
											2016-08-01 05:11:14 +08:00
										 |  |  | 		lockRPCServer.RegisterName("Dsync", lockServer) | 
					
						
							|  |  |  | 		lockRouter := mux.PathPrefix(reservedBucket).Subrouter() | 
					
						
							|  |  |  | 		lockRouter.Path(path.Join("/lock", lockServer.rpcPath)).Handler(lockRPCServer) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } |