diff --git a/cmd/erasure-metadata-utils.go b/cmd/erasure-metadata-utils.go index 6f761e007..befa7e4f2 100644 --- a/cmd/erasure-metadata-utils.go +++ b/cmd/erasure-metadata-utils.go @@ -119,7 +119,7 @@ func readAllFileInfo(ctx context.Context, disks []StorageAPI, bucket, object, ve metadataArray := make([]FileInfo, len(disks)) g := errgroup.WithNErrs(len(disks)) - // Read `xl.meta` parallelly across disks. + // Read `xl.meta` in parallel across disks. for index := range disks { index := index g.Go(func() (err error) { diff --git a/cmd/os-readdir_unix.go b/cmd/os-readdir_unix.go index 2b3589ded..8fec5a7a3 100644 --- a/cmd/os-readdir_unix.go +++ b/cmd/os-readdir_unix.go @@ -19,6 +19,7 @@ package cmd import ( + "bytes" "fmt" "os" "sync" @@ -42,14 +43,14 @@ var direntPool = sync.Pool{ // value used to represent a syscall.DT_UNKNOWN Dirent.Type. const unexpectedFileMode os.FileMode = os.ModeNamedPipe | os.ModeSocket | os.ModeDevice -func parseDirEnt(buf []byte) (consumed int, name string, typ os.FileMode, err error) { +func parseDirEnt(buf []byte) (consumed int, name []byte, typ os.FileMode, err error) { // golang.org/issue/15653 dirent := (*syscall.Dirent)(unsafe.Pointer(&buf[0])) if v := unsafe.Offsetof(dirent.Reclen) + unsafe.Sizeof(dirent.Reclen); uintptr(len(buf)) < v { - return consumed, name, typ, fmt.Errorf("buf size of %d smaller than dirent header size %d", len(buf), v) + return consumed, nil, typ, fmt.Errorf("buf size of %d smaller than dirent header size %d", len(buf), v) } if len(buf) < int(dirent.Reclen) { - return consumed, name, typ, fmt.Errorf("buf size %d < record length %d", len(buf), dirent.Reclen) + return consumed, nil, typ, fmt.Errorf("buf size %d < record length %d", len(buf), dirent.Reclen) } consumed = int(dirent.Reclen) if direntInode(dirent) == 0 { // File absent in directory. @@ -72,11 +73,10 @@ func parseDirEnt(buf []byte) (consumed int, name string, typ os.FileMode, err er nameBuf := (*[unsafe.Sizeof(dirent.Name)]byte)(unsafe.Pointer(&dirent.Name[0])) nameLen, err := direntNamlen(dirent) if err != nil { - return consumed, name, typ, err + return consumed, nil, typ, err } - name = string(nameBuf[:nameLen]) - return consumed, name, typ, nil + return consumed, nameBuf[:nameLen], typ, nil } // Return all the entries at the directory dirPath. @@ -116,13 +116,13 @@ func readDirFilterFn(dirPath string, filter func(name string, typ os.FileMode) e return err } boff += consumed - if name == "" || name == "." || name == ".." { + if len(name) == 0 || bytes.Equal(name, []byte{'.'}) || bytes.Equal(name, []byte{'.', '.'}) { continue } if typ&os.ModeSymlink == os.ModeSymlink { continue } - if err = filter(name, typ); err == errDoneForNow { + if err = filter(string(name), typ); err == errDoneForNow { // filtering requested to return by caller. return nil } @@ -143,6 +143,10 @@ func readDirN(dirPath string, count int) (entries []string, err error) { bufp := direntPool.Get().(*[]byte) defer direntPool.Put(bufp) + nameTmp := direntPool.Get().(*[]byte) + defer direntPool.Put(nameTmp) + tmp := *nameTmp + boff := 0 // starting read position in buf nbuf := 0 // end valid data in buf @@ -165,14 +169,14 @@ func readDirN(dirPath string, count int) (entries []string, err error) { return nil, err } boff += consumed - if name == "" || name == "." || name == ".." { + if len(name) == 0 || bytes.Equal(name, []byte{'.'}) || bytes.Equal(name, []byte{'.', '.'}) { continue } // Fallback for filesystems (like old XFS) that don't // support Dirent.Type and have DT_UNKNOWN (0) there // instead. if typ == unexpectedFileMode { - fi, err := os.Lstat(pathJoin(dirPath, name)) + fi, err := os.Lstat(pathJoin(dirPath, string(name))) if err != nil { // It got deleted in the meantime, not found // or returns too many symlinks ignore this @@ -189,9 +193,13 @@ func readDirN(dirPath string, count int) (entries []string, err error) { continue } if typ.IsRegular() { - entries = append(entries, name) + entries = append(entries, string(name)) } else if typ.IsDir() { - entries = append(entries, name+SlashSeparator) + // Use temp buffer to append a slash to avoid string concat. + tmp = tmp[:len(name)+1] + copy(tmp, name) + tmp[len(tmp)-1] = '/' // SlashSeparator + entries = append(entries, string(tmp)) } count-- } diff --git a/cmd/tree-walk.go b/cmd/tree-walk.go index b88a45d67..f307e31da 100644 --- a/cmd/tree-walk.go +++ b/cmd/tree-walk.go @@ -29,29 +29,20 @@ type TreeWalkResult struct { } // Return entries that have prefix prefixEntry. -// Note: input entries are expected to be sorted. +// The supplied entries are modified and the returned string is a subslice of entries. func filterMatchingPrefix(entries []string, prefixEntry string) []string { - start := 0 - end := len(entries) - for { - if start == end { - break - } - if HasPrefix(entries[start], prefixEntry) { - break - } - start++ + if len(entries) == 0 || prefixEntry == "" { + return entries } - for { - if start == end { - break - } - if HasPrefix(entries[end-1], prefixEntry) { - break + // Write to the beginning of entries. + dst := entries[:0] + for _, s := range entries { + if !HasPrefix(s, prefixEntry) { + continue } - end-- + dst = append(dst, s) } - return entries[start:end] + return dst } // xl.ListDir returns entries with trailing "/" for directories. At the object layer @@ -101,12 +92,12 @@ type IsLeafFunc func(string, string) bool type IsLeafDirFunc func(string, string) bool func filterListEntries(bucket, prefixDir string, entries []string, prefixEntry string, isLeaf IsLeafFunc) ([]string, bool) { - // Listing needs to be sorted. - sort.Strings(entries) - // Filter entries that have the prefix prefixEntry. entries = filterMatchingPrefix(entries, prefixEntry) + // Listing needs to be sorted. + sort.Strings(entries) + // Can isLeaf() check be delayed till when it has to be sent down the // TreeWalkResult channel? delayIsLeaf := delayIsLeafCheck(entries) @@ -124,6 +115,7 @@ func filterListEntries(bucket, prefixDir string, entries []string, prefixEntry s // Sort again after removing trailing "/" for objects as the previous sort // does not hold good anymore. sort.Strings(entries) + return entries, false }