mirror of
https://github.com/minio/minio.git
synced 2025-01-12 07:23:23 -05:00
Enhance listing further, this time handle cases related to common prefixes
This commit is contained in:
parent
5fb46cf75c
commit
c9af01d807
4
Makefile
4
Makefile
@ -35,8 +35,8 @@ lint:
|
|||||||
|
|
||||||
cyclo:
|
cyclo:
|
||||||
@echo "Running $@:"
|
@echo "Running $@:"
|
||||||
@GO15VENDOREXPERIMENT=1 gocyclo -over 50 *.go
|
@GO15VENDOREXPERIMENT=1 gocyclo -over 60 *.go
|
||||||
@GO15VENDOREXPERIMENT=1 gocyclo -over 50 pkg
|
@GO15VENDOREXPERIMENT=1 gocyclo -over 60 pkg
|
||||||
|
|
||||||
build: getdeps verifiers
|
build: getdeps verifiers
|
||||||
@echo "Installing minio:" #@GO15VENDOREXPERIMENT=1 deadcode
|
@echo "Installing minio:" #@GO15VENDOREXPERIMENT=1 deadcode
|
||||||
|
@ -33,13 +33,13 @@ type Metadata struct {
|
|||||||
|
|
||||||
// sortUnique sort a slice in lexical order, removing duplicate elements
|
// sortUnique sort a slice in lexical order, removing duplicate elements
|
||||||
func sortUnique(objects []string) []string {
|
func sortUnique(objects []string) []string {
|
||||||
objectMap := make(map[string]string)
|
results := []string{}
|
||||||
for _, v := range objects {
|
seen := make(map[string]string)
|
||||||
objectMap[v] = v
|
for _, val := range objects {
|
||||||
}
|
if _, ok := seen[val]; !ok {
|
||||||
var results []string
|
results = append(results, val)
|
||||||
for k := range objectMap {
|
seen[val] = val
|
||||||
results = append(results, k)
|
}
|
||||||
}
|
}
|
||||||
sort.Strings(results)
|
sort.Strings(results)
|
||||||
return results
|
return results
|
||||||
|
@ -40,6 +40,10 @@ func (fs API) filterObjects(bucket string, content contentInfo, resources Bucket
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return ObjectMetadata{}, resources, err.Trace()
|
return ObjectMetadata{}, resources, err.Trace()
|
||||||
}
|
}
|
||||||
|
if metadata.Mode.IsDir() {
|
||||||
|
resources.CommonPrefixes = append(resources.CommonPrefixes, name+resources.Delimiter)
|
||||||
|
return ObjectMetadata{}, resources, nil
|
||||||
|
}
|
||||||
case delimitedName == content.FileInfo.Name():
|
case delimitedName == content.FileInfo.Name():
|
||||||
// Use resources.Prefix to filter out delimited files
|
// Use resources.Prefix to filter out delimited files
|
||||||
metadata, err = getMetadata(fs.path, bucket, name)
|
metadata, err = getMetadata(fs.path, bucket, name)
|
||||||
@ -63,6 +67,10 @@ func (fs API) filterObjects(bucket string, content contentInfo, resources Bucket
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return ObjectMetadata{}, resources, err.Trace()
|
return ObjectMetadata{}, resources, err.Trace()
|
||||||
}
|
}
|
||||||
|
if metadata.Mode.IsDir() {
|
||||||
|
resources.CommonPrefixes = append(resources.CommonPrefixes, name+resources.Delimiter)
|
||||||
|
return ObjectMetadata{}, resources, nil
|
||||||
|
}
|
||||||
case delimitedName == content.FileInfo.Name():
|
case delimitedName == content.FileInfo.Name():
|
||||||
metadata, err = getMetadata(fs.path, bucket, name)
|
metadata, err = getMetadata(fs.path, bucket, name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
77
pkg/fs/fs.go
77
pkg/fs/fs.go
@ -17,6 +17,7 @@
|
|||||||
package fs
|
package fs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
@ -93,12 +94,19 @@ func (fs API) DeleteBucket(bucket string) *probe.Error {
|
|||||||
if _, err := os.Stat(bucketDir); os.IsNotExist(err) {
|
if _, err := os.Stat(bucketDir); os.IsNotExist(err) {
|
||||||
return probe.NewError(BucketNotFound{Bucket: bucket})
|
return probe.NewError(BucketNotFound{Bucket: bucket})
|
||||||
}
|
}
|
||||||
files, err := ioutil.ReadDir(bucketDir)
|
var errNotEmpty = errors.New("Directory Not empty")
|
||||||
if err != nil {
|
isDirNotEmpty := func(fp string, fl os.FileInfo, err error) error {
|
||||||
return probe.NewError(err)
|
if fl.Mode().IsRegular() || fl.Mode()&os.ModeSymlink == os.ModeSymlink {
|
||||||
|
return errNotEmpty
|
||||||
|
}
|
||||||
|
return ErrSkipDir
|
||||||
}
|
}
|
||||||
if len(files) > 0 {
|
err := WalkUnsorted(bucketDir, isDirNotEmpty)
|
||||||
return probe.NewError(BucketNotEmpty{Bucket: bucket})
|
if err != nil {
|
||||||
|
if err == errNotEmpty {
|
||||||
|
return probe.NewError(BucketNotEmpty{Bucket: bucket})
|
||||||
|
}
|
||||||
|
return probe.NewError(err)
|
||||||
}
|
}
|
||||||
if err := os.Remove(bucketDir); err != nil {
|
if err := os.Remove(bucketDir); err != nil {
|
||||||
return probe.NewError(err)
|
return probe.NewError(err)
|
||||||
@ -128,7 +136,6 @@ func (fs API) ListBuckets() ([]BucketMetadata, *probe.Error) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
metadata := BucketMetadata{
|
metadata := BucketMetadata{
|
||||||
Name: file.Name(),
|
Name: file.Name(),
|
||||||
Created: file.ModTime(),
|
Created: file.ModTime(),
|
||||||
@ -263,29 +270,67 @@ func (fs API) ListObjects(bucket string, resources BucketResourcesMetadata) ([]O
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If delimiter is supplied make sure that paging doesn't go deep, treat it as simple directory listing.
|
// if delimiter is supplied and not prefix then we are the very top level, list everything and move on.
|
||||||
if resources.Delimiter != "" {
|
if resources.Delimiter != "" && resources.Prefix == "" {
|
||||||
files, err := ioutil.ReadDir(filepath.Join(rootPrefix, resources.Prefix))
|
files, err := ioutil.ReadDir(rootPrefix)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
return nil, resources, probe.NewError(ObjectNotFound{Bucket: bucket, Object: resources.Prefix})
|
return nil, resources, probe.NewError(BucketNotFound{Bucket: bucket})
|
||||||
}
|
}
|
||||||
return nil, resources, probe.NewError(err)
|
return nil, resources, probe.NewError(err)
|
||||||
}
|
}
|
||||||
for _, fl := range files {
|
for _, fl := range files {
|
||||||
prefix := fl.Name()
|
|
||||||
if resources.Prefix != "" {
|
|
||||||
prefix = filepath.Join(resources.Prefix, fl.Name())
|
|
||||||
}
|
|
||||||
p.files = append(p.files, contentInfo{
|
p.files = append(p.files, contentInfo{
|
||||||
Prefix: prefix,
|
Prefix: fl.Name(),
|
||||||
Size: fl.Size(),
|
Size: fl.Size(),
|
||||||
Mode: fl.Mode(),
|
Mode: fl.Mode(),
|
||||||
ModTime: fl.ModTime(),
|
ModTime: fl.ModTime(),
|
||||||
FileInfo: fl,
|
FileInfo: fl,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
|
||||||
|
// If delimiter and prefix is supplied make sure that paging doesn't go deep, treat it as simple directory listing.
|
||||||
|
if resources.Delimiter != "" && resources.Prefix != "" {
|
||||||
|
if !strings.HasSuffix(resources.Prefix, resources.Delimiter) {
|
||||||
|
fl, err := os.Stat(filepath.Join(rootPrefix, resources.Prefix))
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return nil, resources, probe.NewError(ObjectNotFound{Bucket: bucket, Object: resources.Prefix})
|
||||||
|
}
|
||||||
|
return nil, resources, probe.NewError(err)
|
||||||
|
}
|
||||||
|
p.files = append(p.files, contentInfo{
|
||||||
|
Prefix: resources.Prefix,
|
||||||
|
Size: fl.Size(),
|
||||||
|
Mode: os.ModeDir,
|
||||||
|
ModTime: fl.ModTime(),
|
||||||
|
FileInfo: fl,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
files, err := ioutil.ReadDir(filepath.Join(rootPrefix, resources.Prefix))
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return nil, resources, probe.NewError(ObjectNotFound{Bucket: bucket, Object: resources.Prefix})
|
||||||
|
}
|
||||||
|
return nil, resources, probe.NewError(err)
|
||||||
|
}
|
||||||
|
for _, fl := range files {
|
||||||
|
prefix := fl.Name()
|
||||||
|
if resources.Prefix != "" {
|
||||||
|
prefix = filepath.Join(resources.Prefix, fl.Name())
|
||||||
|
}
|
||||||
|
p.files = append(p.files, contentInfo{
|
||||||
|
Prefix: prefix,
|
||||||
|
Size: fl.Size(),
|
||||||
|
Mode: fl.Mode(),
|
||||||
|
ModTime: fl.ModTime(),
|
||||||
|
FileInfo: fl,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if resources.Delimiter == "" {
|
||||||
var files []contentInfo
|
var files []contentInfo
|
||||||
getAllFiles := func(fp string, fl os.FileInfo, err error) error {
|
getAllFiles := func(fp string, fl os.FileInfo, err error) error {
|
||||||
// If any error return back quickly
|
// If any error return back quickly
|
||||||
|
@ -33,9 +33,28 @@ func Walk(root string, walkFn WalkFunc) error {
|
|||||||
return walk(root, info, walkFn)
|
return walk(root, info, walkFn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WalkUnsorted walks the file tree rooted at root, calling walkFn for each file or
|
||||||
|
// directory in the tree, including root.
|
||||||
|
func WalkUnsorted(root string, walkFn WalkFunc) error {
|
||||||
|
info, err := os.Lstat(root)
|
||||||
|
if err != nil {
|
||||||
|
return walkFn(root, nil, err)
|
||||||
|
}
|
||||||
|
return walk(root, info, walkFn)
|
||||||
|
}
|
||||||
|
|
||||||
// readDirNames reads the directory named by dirname and returns
|
// readDirNames reads the directory named by dirname and returns
|
||||||
// a sorted list of directory entries.
|
// a sorted list of directory entries.
|
||||||
func readDirNames(dirname string) ([]string, error) {
|
func readDirNames(dirname string) ([]string, error) {
|
||||||
|
names, err := readDirUnsortedNames(dirname)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
sort.Strings(names)
|
||||||
|
return names, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func readDirUnsortedNames(dirname string) ([]string, error) {
|
||||||
f, err := os.Open(dirname)
|
f, err := os.Open(dirname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -45,7 +64,6 @@ func readDirNames(dirname string) ([]string, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
sort.Strings(names)
|
|
||||||
return names, nil
|
return names, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,6 +84,46 @@ var ErrSkipDir = errors.New("skip this directory")
|
|||||||
// as an error by any function.
|
// as an error by any function.
|
||||||
var ErrSkipFile = errors.New("skip this file")
|
var ErrSkipFile = errors.New("skip this file")
|
||||||
|
|
||||||
|
func walkUnsorted(path string, info os.FileInfo, walkFn WalkFunc) error {
|
||||||
|
err := walkFn(path, info, nil)
|
||||||
|
if err != nil {
|
||||||
|
if info.Mode().IsDir() && err == ErrSkipDir {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if info.Mode().IsRegular() && err == ErrSkipFile {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !info.IsDir() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
names, err := readDirUnsortedNames(path)
|
||||||
|
if err != nil {
|
||||||
|
return walkFn(path, info, err)
|
||||||
|
}
|
||||||
|
for _, name := range names {
|
||||||
|
filename := filepath.Join(path, name)
|
||||||
|
fileInfo, err := os.Lstat(filename)
|
||||||
|
if err != nil {
|
||||||
|
if err := walkFn(filename, fileInfo, err); err != nil && err != ErrSkipDir && err != ErrSkipFile {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = walk(filename, fileInfo, walkFn)
|
||||||
|
if err != nil {
|
||||||
|
if err == ErrSkipDir || err == ErrSkipFile {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// walk recursively descends path, calling w.
|
// walk recursively descends path, calling w.
|
||||||
func walk(path string, info os.FileInfo, walkFn WalkFunc) error {
|
func walk(path string, info os.FileInfo, walkFn WalkFunc) error {
|
||||||
err := walkFn(path, info, nil)
|
err := walkFn(path, info, nil)
|
||||||
|
Loading…
Reference in New Issue
Block a user