mirror of
https://github.com/minio/minio.git
synced 2025-01-11 15:03:22 -05:00
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.
This commit is contained in:
parent
b6ebf2aba8
commit
533338bdeb
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
}
|
@ -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 {
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user