diff --git a/cmd/object-common.go b/cmd/object-common.go index 02d6e87fd..df3f7d4a6 100644 --- a/cmd/object-common.go +++ b/cmd/object-common.go @@ -56,18 +56,21 @@ func fsHouseKeeping(storageDisk StorageAPI) error { // Check if a network path is local to this node. func isLocalStorage(networkPath string) bool { if idx := strings.LastIndex(networkPath, ":"); idx != -1 { - // e.g 10.0.0.1:9000:/mnt/networkPath - netAddr, _ := splitNetPath(networkPath) - var netHost string - var err error - netHost, _, err = net.SplitHostPort(netAddr) + // e.g 10.0.0.1:/mnt/networkPath + netAddr, _, err := splitNetPath(networkPath) if err != nil { - netHost = netAddr + errorIf(err, "Splitting into ip and path failed") + return false + } + // netAddr will only be set if this is not a local path. + if netAddr == "" { + return true } // Resolve host to address to check if the IP is loopback. // If address resolution fails, assume it's a non-local host. - addrs, err := net.LookupHost(netHost) + addrs, err := net.LookupHost(netAddr) if err != nil { + errorIf(err, "Failed to lookup host") return false } for _, addr := range addrs { @@ -77,12 +80,14 @@ func isLocalStorage(networkPath string) bool { } iaddrs, err := net.InterfaceAddrs() if err != nil { + errorIf(err, "Unable to list interface addresses") return false } for _, addr := range addrs { for _, iaddr := range iaddrs { ip, _, err := net.ParseCIDR(iaddr.String()) if err != nil { + errorIf(err, "Unable to parse CIDR") return false } if ip.String() == addr { diff --git a/cmd/storage-rpc-client.go b/cmd/storage-rpc-client.go index 8da757281..a77cd858e 100644 --- a/cmd/storage-rpc-client.go +++ b/cmd/storage-rpc-client.go @@ -36,14 +36,6 @@ const ( storageRPCPath = reservedBucket + "/storage" ) -// splits network path into its components Address and Path. -func splitNetPath(networkPath string) (netAddr, netPath string) { - index := strings.LastIndex(networkPath, ":") - netAddr = networkPath[:index] - netPath = networkPath[index+1:] - return netAddr, netPath -} - // Converts rpc.ServerError to underlying error. This function is // written so that the storageAPI errors are consistent across network // disks as well. @@ -111,7 +103,10 @@ func newRPCClient(networkPath string) (StorageAPI, error) { } // TODO validate netAddr and netPath. - netAddr, netPath := splitNetPath(networkPath) + netAddr, netPath, err := splitNetPath(networkPath) + if err != nil { + return nil, err + } // Dial minio rpc storage http path. rpcPath := path.Join(storageRPCPath, netPath) diff --git a/cmd/utils.go b/cmd/utils.go index 29897906c..46e6d0a89 100644 --- a/cmd/utils.go +++ b/cmd/utils.go @@ -21,9 +21,12 @@ import ( "encoding/xml" "errors" "io" + "net" "net/http" "os" "os/exec" + "path/filepath" + "runtime" "strings" "sync" "syscall" @@ -43,6 +46,28 @@ func cloneHeader(h http.Header) http.Header { return h2 } +// splits network path into its components Address and Path. +func splitNetPath(networkPath string) (netAddr, netPath string, err error) { + if runtime.GOOS == "windows" { + if volumeName := filepath.VolumeName(networkPath); volumeName != "" { + return "", networkPath, nil + } + } + networkParts := strings.SplitN(networkPath, ":", 2) + switch len(networkParts) { + case 1: + return "", networkPath, nil + case 2: + if networkParts[1] == "" { + return "", "", &net.AddrError{Err: "missing path in network path", Addr: networkPath} + } else if networkParts[0] == "" { + return "", "", &net.AddrError{Err: "missing address in network path", Addr: networkPath} + } + return networkParts[0], networkParts[1], nil + } + return networkParts[0], networkParts[1], nil +} + // xmlDecoder provide decoded value in xml. func xmlDecoder(body io.Reader, v interface{}, size int64) error { var lbody io.Reader diff --git a/rpc-client.go b/rpc-client.go index 6a2e9cea5..adb24f1c9 100644 --- a/rpc-client.go +++ b/rpc-client.go @@ -43,6 +43,10 @@ func newClient(node, rpcPath string) *RPCClient { func (rpcClient *RPCClient) Close() error { rpcClient.Lock() defer rpcClient.Unlock() + // If rpc client has not connected yet there is nothing to close. + if rpcClient.rpc == nil { + return nil + } // Reset rpcClient.rpc to allow for subsequent calls to use a new // (socket) connection. clnt := rpcClient.rpc diff --git a/utils_nix_test.go b/utils_nix_test.go new file mode 100644 index 000000000..f6c4e65b3 --- /dev/null +++ b/utils_nix_test.go @@ -0,0 +1,55 @@ +// +build !windows + +/* + * 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. + */ + +package main + +import ( + "net" + "testing" +) + +// Test for splitNetPath +func TestSplitNetPath(t *testing.T) { + testCases := []struct { + networkPath string + netAddr string + netPath string + err error + }{ + {"10.1.10.1:", "", "", &net.AddrError{Err: "missing path in network path", Addr: "10.1.10.1:"}}, + {"10.1.10.1", "", "10.1.10.1", nil}, + {"10.1.10.1://", "10.1.10.1", "//", nil}, + {"10.1.10.1:/disk/1", "10.1.10.1", "/disk/1", nil}, + {"10.1.10.1:\\path\\test", "10.1.10.1", "\\path\\test", nil}, + } + + for i, test := range testCases { + receivedAddr, receivedPath, receivedErr := splitNetPath(test.networkPath) + if receivedAddr != test.netAddr { + t.Errorf("Test case %d: Expected: %s, Received: %s", i+1, test.netAddr, receivedAddr) + } + if receivedPath != test.netPath { + t.Errorf("Test case %d: Expected: %s, Received: %s", i+1, test.netPath, receivedPath) + } + if test.err != nil { + if receivedErr == nil || receivedErr.Error() != test.err.Error() { + t.Errorf("Test case %d: Expected: %v, Received: %v", i+1, test.err, receivedErr) + } + } + } +} diff --git a/utils_windows_test.go b/utils_windows_test.go new file mode 100644 index 000000000..821c2d0b0 --- /dev/null +++ b/utils_windows_test.go @@ -0,0 +1,55 @@ +// +build windows + +/* + * Minio Cloud Storage, (C) 2015 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. + */ + +package main + +import ( + "net" + "testing" +) + +// Test for splitNetPath +func TestSplitNetPath(t *testing.T) { + testCases := []struct { + networkPath string + netAddr string + netPath string + err error + }{ + {"10.1.10.1:C:\\path\\test", "10.1.10.1", "C:\\path\\test", nil}, + {"10.1.10.1:C:", "10.1.10.1", "C:", nil}, + {":C:", "", "", &net.AddrError{Err: "missing address in network path", Addr: ":C:"}}, + {"C:\\path\\test", "", "C:\\path\\test", nil}, + {"10.1.10.1::C:\\path\\test", "10.1.10.1", ":C:\\path\\test", nil}, + } + + for i, test := range testCases { + receivedAddr, receivedPath, receivedErr := splitNetPath(test.networkPath) + if receivedAddr != test.netAddr { + t.Errorf("Test case %d: Expected: %s, Received: %s", i+1, test.netAddr, receivedAddr) + } + if receivedPath != test.netPath { + t.Errorf("Test case %d: Expected: %s, Received: %s", i+1, test.netPath, receivedPath) + } + if test.err != nil { + if receivedErr == nil || receivedErr.Error() != test.err.Error() { + t.Errorf("Test case %d: Expected: %v, Received: %v", i+1, test.err, receivedErr) + } + } + } +}