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.
154 lines
3.8 KiB
Go
154 lines
3.8 KiB
Go
// +build linux darwin dragonfly freebsd netbsd openbsd
|
|
|
|
/*
|
|
* 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 (
|
|
"os"
|
|
"path"
|
|
"runtime"
|
|
"strings"
|
|
"sync"
|
|
"syscall"
|
|
"unsafe"
|
|
)
|
|
|
|
const (
|
|
// readDirentBufSize for syscall.ReadDirent() to hold multiple
|
|
// directory entries in one buffer. golang source uses 4096 as
|
|
// buffer size whereas we want 64 times larger to save lots of
|
|
// entries to avoid multiple syscall.ReadDirent() call.
|
|
readDirentBufSize = 4096 * 64
|
|
)
|
|
|
|
// actual length of the byte array from the c - world.
|
|
func clen(n []byte) int {
|
|
for i := 0; i < len(n); i++ {
|
|
if n[i] == 0 {
|
|
return i
|
|
}
|
|
}
|
|
return len(n)
|
|
}
|
|
|
|
// parseDirents - inspired from
|
|
// https://golang.org/src/syscall/syscall_<os>.go
|
|
func parseDirents(dirPath string, buf []byte) (entries []string, err error) {
|
|
bufidx := 0
|
|
for bufidx < len(buf) {
|
|
dirent := (*syscall.Dirent)(unsafe.Pointer(&buf[bufidx]))
|
|
// On non-Linux operating systems for rec length of zero means
|
|
// we have reached EOF break out.
|
|
if runtime.GOOS != "linux" && dirent.Reclen == 0 {
|
|
break
|
|
}
|
|
bufidx += int(dirent.Reclen)
|
|
// Skip if they are absent in directory.
|
|
if isEmptyDirent(dirent) {
|
|
continue
|
|
}
|
|
bytes := (*[10000]byte)(unsafe.Pointer(&dirent.Name[0]))
|
|
var name = string(bytes[0:clen(bytes[:])])
|
|
// Reserved names skip them.
|
|
if name == "." || name == ".." {
|
|
continue
|
|
}
|
|
|
|
switch dirent.Type {
|
|
case syscall.DT_DIR:
|
|
entries = append(entries, name+slashSeparator)
|
|
case syscall.DT_REG:
|
|
entries = append(entries, name)
|
|
case syscall.DT_LNK, syscall.DT_UNKNOWN:
|
|
// If its symbolic link, follow the link using os.Stat()
|
|
|
|
// On Linux XFS does not implement d_type for on disk
|
|
// format << v5. Fall back to Stat().
|
|
var fi os.FileInfo
|
|
fi, err = os.Stat(path.Join(dirPath, name))
|
|
if err != nil {
|
|
// If file does not exist, we continue and skip it.
|
|
// Could happen if it was deleted in the middle while
|
|
// this list was being performed.
|
|
if os.IsNotExist(err) {
|
|
continue
|
|
}
|
|
return nil, err
|
|
}
|
|
if fi.IsDir() {
|
|
entries = append(entries, fi.Name()+slashSeparator)
|
|
} else if fi.Mode().IsRegular() {
|
|
entries = append(entries, fi.Name())
|
|
}
|
|
default:
|
|
// Skip entries which are not file or directory.
|
|
continue
|
|
}
|
|
}
|
|
return entries, nil
|
|
}
|
|
|
|
var readDirBufPool = sync.Pool{
|
|
New: func() interface{} {
|
|
b := make([]byte, readDirentBufSize)
|
|
return &b
|
|
},
|
|
}
|
|
|
|
// Return all the entries at the directory dirPath.
|
|
func readDir(dirPath string) (entries []string, err error) {
|
|
bufp := readDirBufPool.Get().(*[]byte)
|
|
buf := *bufp
|
|
defer readDirBufPool.Put(bufp)
|
|
|
|
d, err := os.Open(dirPath)
|
|
if err != nil {
|
|
// File is really not found.
|
|
if os.IsNotExist(err) {
|
|
return nil, errFileNotFound
|
|
}
|
|
if os.IsPermission(err) {
|
|
return nil, errFileAccessDenied
|
|
}
|
|
|
|
// File path cannot be verified since one of the parents is a file.
|
|
if strings.Contains(err.Error(), "not a directory") {
|
|
return nil, errFileNotFound
|
|
}
|
|
return nil, err
|
|
}
|
|
defer d.Close()
|
|
|
|
fd := int(d.Fd())
|
|
for {
|
|
nbuf, err := syscall.ReadDirent(fd, buf)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if nbuf <= 0 {
|
|
break
|
|
}
|
|
var tmpEntries []string
|
|
if tmpEntries, err = parseDirents(dirPath, buf[:nbuf]); err != nil {
|
|
return nil, err
|
|
}
|
|
entries = append(entries, tmpEntries...)
|
|
}
|
|
return
|
|
}
|