From 533338bdebd1ca082793684ea418b9d23738dcf4 Mon Sep 17 00:00:00 2001 From: Harshavardhana Date: Fri, 3 Feb 2017 23:27:50 -0800 Subject: [PATCH] all/windows: Be case in-sensitive about pattern matching. (#3682) Resource strings and paths are case insensitive on windows deployments but if user happens to use upper case instead of lower case for certain configuration params like bucket policies and bucket notification config. We might not honor them which leads to a wrong behavior on windows. This is windows only behavior, for all other platforms case is still kept sensitive. --- cmd/bucket-handlers-listobjects.go | 3 +-- cmd/bucket-handlers.go | 2 +- cmd/bucket-policy-handlers.go | 6 +++++ cmd/bucket-policy-parser.go | 6 ++--- cmd/generic-handlers.go | 4 ++-- cmd/lockinfo-handlers.go | 7 ++---- cmd/notifiers.go | 5 ++-- cmd/object-api-input-checks.go | 10 +++----- cmd/object-api-utils.go | 25 ++++++++++++++++++-- cmd/posix-list-dir-nix.go | 4 ---- cmd/posix-list-dir-others.go | 4 ---- cmd/posix-list-dir_test.go | 22 ------------------ cmd/posix-utils_common.go | 37 ------------------------------ cmd/tree-walk.go | 8 +++---- cmd/tree-walk_test.go | 2 +- 15 files changed, 48 insertions(+), 97 deletions(-) delete mode 100644 cmd/posix-utils_common.go diff --git a/cmd/bucket-handlers-listobjects.go b/cmd/bucket-handlers-listobjects.go index 81db8b65c..6381ce837 100644 --- a/cmd/bucket-handlers-listobjects.go +++ b/cmd/bucket-handlers-listobjects.go @@ -18,7 +18,6 @@ package cmd import ( "net/http" - "strings" "github.com/gorilla/mux" ) @@ -44,7 +43,7 @@ func validateListObjectsArgs(prefix, marker, delimiter string, maxKeys int) APIE // Marker is set validate pre-condition. if marker != "" { // Marker not common with prefix is not implemented. - if !strings.HasPrefix(marker, prefix) { + if !hasPrefix(marker, prefix) { return ErrNotImplemented } } diff --git a/cmd/bucket-handlers.go b/cmd/bucket-handlers.go index e6f5c6871..660396a3a 100644 --- a/cmd/bucket-handlers.go +++ b/cmd/bucket-handlers.go @@ -165,7 +165,7 @@ func (api objectAPIHandlers) ListMultipartUploadsHandler(w http.ResponseWriter, } if keyMarker != "" { // Marker not common with prefix is not implemented. - if !strings.HasPrefix(keyMarker, prefix) { + if !hasPrefix(keyMarker, prefix) { writeErrorResponse(w, ErrNotImplemented, r.URL) return } diff --git a/cmd/bucket-policy-handlers.go b/cmd/bucket-policy-handlers.go index 00b3b5d2b..d68e2537f 100644 --- a/cmd/bucket-policy-handlers.go +++ b/cmd/bucket-policy-handlers.go @@ -21,6 +21,8 @@ import ( "io" "io/ioutil" "net/http" + "runtime" + "strings" humanize "github.com/dustin/go-humanize" mux "github.com/gorilla/mux" @@ -63,6 +65,10 @@ func bucketPolicyActionMatch(action string, statement policyStatement) bool { // Match function matches wild cards in 'pattern' for resource. func resourceMatch(pattern, resource string) bool { + if runtime.GOOS == "windows" { + // For windows specifically make sure we are case insensitive. + return wildcard.Match(strings.ToLower(pattern), strings.ToLower(resource)) + } return wildcard.Match(pattern, resource) } diff --git a/cmd/bucket-policy-parser.go b/cmd/bucket-policy-parser.go index 7af5e5bf4..5d98b8690 100644 --- a/cmd/bucket-policy-parser.go +++ b/cmd/bucket-policy-parser.go @@ -111,12 +111,12 @@ func isValidResources(resources set.StringSet) (err error) { return err } for resource := range resources { - if !strings.HasPrefix(resource, bucketARNPrefix) { + if !hasPrefix(resource, bucketARNPrefix) { err = errors.New("Unsupported resource style found: ‘" + resource + "’, please validate your policy document") return err } resourceSuffix := strings.SplitAfter(resource, bucketARNPrefix)[1] - if len(resourceSuffix) == 0 || strings.HasPrefix(resourceSuffix, "/") { + if len(resourceSuffix) == 0 || hasPrefix(resourceSuffix, "/") { err = errors.New("Invalid resource style found: ‘" + resource + "’, please validate your policy document") return err } @@ -282,7 +282,7 @@ func checkBucketPolicyResources(bucket string, bucketPolicy *bucketPolicy) APIEr // nesting. Reject such rules. for _, otherResource := range resources { // Common prefix reject such rules. - if strings.HasPrefix(otherResource, resource) { + if hasPrefix(otherResource, resource) { return ErrPolicyNesting } } diff --git a/cmd/generic-handlers.go b/cmd/generic-handlers.go index 974045687..229e752b4 100644 --- a/cmd/generic-handlers.go +++ b/cmd/generic-handlers.go @@ -141,8 +141,8 @@ func setBrowserCacheControlHandler(h http.Handler) http.Handler { func (h cacheControlHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { if r.Method == httpGET && guessIsBrowserReq(r) && globalIsBrowserEnabled { // For all browser requests set appropriate Cache-Control policies - if strings.HasPrefix(r.URL.Path, reservedBucket+"/") { - if strings.HasSuffix(r.URL.Path, ".js") || r.URL.Path == reservedBucket+"/favicon.ico" { + if hasPrefix(r.URL.Path, reservedBucket+"/") { + if hasSuffix(r.URL.Path, ".js") || r.URL.Path == reservedBucket+"/favicon.ico" { // For assets set cache expiry of one year. For each release, the name // of the asset name will change and hence it can not be served from cache. w.Header().Set("Cache-Control", "max-age=31536000") diff --git a/cmd/lockinfo-handlers.go b/cmd/lockinfo-handlers.go index 3a9e093f9..aa3fb234d 100644 --- a/cmd/lockinfo-handlers.go +++ b/cmd/lockinfo-handlers.go @@ -16,10 +16,7 @@ package cmd -import ( - "strings" - "time" -) +import "time" // SystemLockState - Structure to fill the lock state of entire object storage. // That is the total locks held, total calls blocked on locks and state of all the locks for the entire system. @@ -82,7 +79,7 @@ func listLocksInfo(bucket, prefix string, duration time.Duration) []VolumeLockIn continue } // N B empty prefix matches all param.path. - if !strings.HasPrefix(param.path, prefix) { + if !hasPrefix(param.path, prefix) { continue } diff --git a/cmd/notifiers.go b/cmd/notifiers.go index b7b5ec496..f1f1c4cbc 100644 --- a/cmd/notifiers.go +++ b/cmd/notifiers.go @@ -18,7 +18,6 @@ package cmd import ( "errors" - "strings" "github.com/minio/minio/pkg/wildcard" ) @@ -206,9 +205,9 @@ func filterRuleMatch(object string, frs []filterRule) bool { var prefixMatch, suffixMatch = true, true for _, fr := range frs { if isValidFilterNamePrefix(fr.Name) { - prefixMatch = strings.HasPrefix(object, fr.Value) + prefixMatch = hasPrefix(object, fr.Value) } else if isValidFilterNameSuffix(fr.Name) { - suffixMatch = strings.HasSuffix(object, fr.Value) + suffixMatch = hasSuffix(object, fr.Value) } } return prefixMatch && suffixMatch diff --git a/cmd/object-api-input-checks.go b/cmd/object-api-input-checks.go index 74b736e54..62e77ab16 100644 --- a/cmd/object-api-input-checks.go +++ b/cmd/object-api-input-checks.go @@ -16,11 +16,7 @@ package cmd -import ( - "strings" - - "github.com/skyrings/skyring-common/tools/uuid" -) +import "github.com/skyrings/skyring-common/tools/uuid" // Checks on GetObject arguments, bucket and object. func checkGetObjArgs(bucket, object string) error { @@ -69,7 +65,7 @@ func checkListObjsArgs(bucket, prefix, marker, delimiter string, obj ObjectLayer }) } // Verify if marker has prefix. - if marker != "" && !strings.HasPrefix(marker, prefix) { + if marker != "" && !hasPrefix(marker, prefix) { return traceError(InvalidMarkerPrefixCombination{ Marker: marker, Prefix: prefix, @@ -84,7 +80,7 @@ func checkListMultipartArgs(bucket, prefix, keyMarker, uploadIDMarker, delimiter return err } if uploadIDMarker != "" { - if strings.HasSuffix(keyMarker, slashSeparator) { + if hasSuffix(keyMarker, slashSeparator) { return traceError(InvalidUploadIDKeyCombination{ UploadIDMarker: uploadIDMarker, KeyMarker: keyMarker, diff --git a/cmd/object-api-utils.go b/cmd/object-api-utils.go index 95f63d339..206c0c1a0 100644 --- a/cmd/object-api-utils.go +++ b/cmd/object-api-utils.go @@ -22,6 +22,7 @@ import ( "io" "path" "regexp" + "runtime" "strings" "unicode/utf8" @@ -91,10 +92,10 @@ func IsValidObjectName(object string) bool { if len(object) == 0 { return false } - if strings.HasSuffix(object, slashSeparator) { + if hasSuffix(object, slashSeparator) { return false } - if strings.HasPrefix(object, slashSeparator) { + if hasPrefix(object, slashSeparator) { return false } return IsValidObjectPrefix(object) @@ -159,6 +160,26 @@ func getCompleteMultipartMD5(parts []completePart) (string, error) { return s3MD5, nil } +// Prefix matcher string matches prefix in a platform specific way. +// For example on windows since its case insensitive we are supposed +// to do case insensitive checks. +func hasPrefix(s string, prefix string) bool { + if runtime.GOOS == "windows" { + return strings.HasPrefix(strings.ToLower(s), strings.ToLower(prefix)) + } + return strings.HasPrefix(s, prefix) +} + +// Suffix matcher string matches suffix in a platform specific way. +// For example on windows since its case insensitive we are supposed +// to do case insensitive checks. +func hasSuffix(s string, suffix string) bool { + if runtime.GOOS == "windows" { + return strings.HasSuffix(strings.ToLower(s), strings.ToLower(suffix)) + } + return strings.HasSuffix(s, suffix) +} + // byBucketName is a collection satisfying sort.Interface. type byBucketName []BucketInfo diff --git a/cmd/posix-list-dir-nix.go b/cmd/posix-list-dir-nix.go index 62421abce..1da7eaf76 100644 --- a/cmd/posix-list-dir-nix.go +++ b/cmd/posix-list-dir-nix.go @@ -68,10 +68,6 @@ func parseDirents(dirPath string, buf []byte) (entries []string, err error) { if name == "." || name == ".." { continue } - // Skip special files. - if hasPosixReservedPrefix(name) { - continue - } switch dirent.Type { case syscall.DT_DIR: diff --git a/cmd/posix-list-dir-others.go b/cmd/posix-list-dir-others.go index abd3d965b..dd40faca5 100644 --- a/cmd/posix-list-dir-others.go +++ b/cmd/posix-list-dir-others.go @@ -52,10 +52,6 @@ func readDir(dirPath string) (entries []string, err error) { return nil, err } for _, fi := range fis { - // Skip special files, if found. - if hasPosixReservedPrefix(fi.Name()) { - continue - } // Stat symbolic link and follow to get the final value. if fi.Mode()&os.ModeSymlink == os.ModeSymlink { var st os.FileInfo diff --git a/cmd/posix-list-dir_test.go b/cmd/posix-list-dir_test.go index c709f7ec5..30d85b862 100644 --- a/cmd/posix-list-dir_test.go +++ b/cmd/posix-list-dir_test.go @@ -69,26 +69,6 @@ func setupTestReadDirEmpty(t *testing.T) (testResults []result) { return testResults } -// Test to read empty directory with only reserved names. -func setupTestReadDirReserved(t *testing.T) (testResults []result) { - dir := mustSetupDir(t) - entries := []string{} - // Create a file with reserved name. - for _, reservedName := range posixReservedPrefix { - if err := ioutil.WriteFile(filepath.Join(dir, reservedName), []byte{}, os.ModePerm); err != nil { - // For cleanup, its required to add these entries into test results. - testResults = append(testResults, result{dir, entries}) - t.Fatalf("Unable to create file, %s", err) - } - // entries = append(entries, reservedName) - reserved files are skipped. - } - sort.Strings(entries) - - // Add entries slice for this test directory. - testResults = append(testResults, result{dir, entries}) - return testResults -} - // Test to read non-empty directory with only files. func setupTestReadDirFiles(t *testing.T) (testResults []result) { dir := mustSetupDir(t) @@ -198,8 +178,6 @@ func TestReadDir(t *testing.T) { // Setup and capture test results for empty directory. testResults = append(testResults, setupTestReadDirEmpty(t)...) - // Setup and capture test results for reserved files. - testResults = append(testResults, setupTestReadDirReserved(t)...) // Setup and capture test results for directory with only files. testResults = append(testResults, setupTestReadDirFiles(t)...) // Setup and capture test results for directory with files and directories. diff --git a/cmd/posix-utils_common.go b/cmd/posix-utils_common.go deleted file mode 100644 index ea15b8bca..000000000 --- a/cmd/posix-utils_common.go +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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 cmd - -import "strings" - -// List of reserved words for files, includes old and new ones. -var posixReservedPrefix = []string{ - "$tmpfile", - // Add new reserved words if any used in future. -} - -// hasPosixReservedPrefix - has reserved prefix. -func hasPosixReservedPrefix(name string) (isReserved bool) { - for _, reservedKey := range posixReservedPrefix { - if strings.HasPrefix(name, reservedKey) { - isReserved = true - break - } - } - - return isReserved -} diff --git a/cmd/tree-walk.go b/cmd/tree-walk.go index 8993a96d0..05c78b0cd 100644 --- a/cmd/tree-walk.go +++ b/cmd/tree-walk.go @@ -67,7 +67,7 @@ func filterMatchingPrefix(entries []string, prefixEntry string) []string { if start == end { break } - if strings.HasPrefix(entries[start], prefixEntry) { + if hasPrefix(entries[start], prefixEntry) { break } start++ @@ -76,7 +76,7 @@ func filterMatchingPrefix(entries []string, prefixEntry string) []string { if start == end { break } - if strings.HasPrefix(entries[end-1], prefixEntry) { + if hasPrefix(entries[end-1], prefixEntry) { break } end-- @@ -173,7 +173,7 @@ func doTreeWalk(bucket, prefixDir, entryPrefixMatch, marker string, recursive bo // Skip as the marker would already be listed in the previous listing. continue } - if recursive && !strings.HasSuffix(entry, slashSeparator) { + if recursive && !hasSuffix(entry, slashSeparator) { // We should not skip for recursive listing and if markerDir is a directory // for ex. if marker is "four/five.txt" markerDir will be "four/" which // should not be skipped, instead it will need to be treeWalk()'ed into. @@ -182,7 +182,7 @@ func doTreeWalk(bucket, prefixDir, entryPrefixMatch, marker string, recursive bo continue } } - if recursive && strings.HasSuffix(entry, slashSeparator) { + if recursive && hasSuffix(entry, slashSeparator) { // If the entry is a directory, we will need recurse into it. markerArg := "" if entry == markerDir { diff --git a/cmd/tree-walk_test.go b/cmd/tree-walk_test.go index 939439f2f..9490c6c56 100644 --- a/cmd/tree-walk_test.go +++ b/cmd/tree-walk_test.go @@ -135,7 +135,7 @@ func testTreeWalkPrefix(t *testing.T, listDir listDirFunc, isLeaf isLeafFunc) { // Check if all entries received on the channel match the prefix. for res := range twResultCh { - if !strings.HasPrefix(res.entry, prefix) { + if !hasPrefix(res.entry, prefix) { t.Errorf("Entry %s doesn't match prefix %s", res.entry, prefix) } }