Created
May 24, 2016 08:36
-
-
Save krishnasrinivas/ee4caa17dbe6af00eb17a5af10182d3d to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"fmt" | |
"log" | |
"os" | |
"path" | |
"runtime" | |
"strings" | |
"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 25 times larger to save lots of | |
// entries to avoid multiple syscall.ReadDirent() call. | |
readDirentBufSize = 4096 * 25 | |
) | |
// 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) | |
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+"/") | |
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) { | |
err = nil | |
continue | |
} | |
return nil, err | |
} | |
if fi.IsDir() { | |
entries = append(entries, fi.Name()+"/") | |
} else if fi.Mode().IsRegular() { | |
entries = append(entries, fi.Name()) | |
} | |
default: | |
// Skip entries which are not file or directory. | |
continue | |
} | |
} | |
return entries, nil | |
} | |
// Return all the entries at the directory dirPath. | |
func readDir(dirPath string) (entries []string, err error) { | |
buf := make([]byte, readDirentBufSize) | |
d, err := os.Open(dirPath) | |
if err != nil { | |
// File is really not found. | |
if os.IsNotExist(err) { | |
return nil, err | |
} | |
// File path cannot be verified since one of the parents is a file. | |
if strings.Contains(err.Error(), "not a directory") { | |
return nil, err | |
} | |
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 | |
} | |
func main() { | |
entries, err := readDir(".") | |
if err != nil { | |
log.Fatal(err) | |
} | |
for _, entry := range entries { | |
_, err := os.Stat(entry + "/xl.json") | |
if err != nil { | |
log.Fatal(err) | |
} | |
} | |
fmt.Println(len(entries)) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment