2019-03-06 15:03:21 +01:00
|
|
|
package sizer
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"sync"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
// FileType is a const representing a file
|
|
|
|
FileType = iota
|
|
|
|
// DirType is a const representing a directory
|
|
|
|
DirType = iota
|
|
|
|
// SymLinkType is a const representing a symbolic link
|
|
|
|
SymLinkType = iota
|
|
|
|
// HardLinkType is a const representing a hard link
|
|
|
|
HardLinkType = iota
|
|
|
|
// CharDevType is a const representing a char device
|
|
|
|
CharDevType = iota
|
|
|
|
// NamedPipeType is a const representing a named pipe
|
|
|
|
NamedPipeType = iota
|
|
|
|
// SocketType is a const representing a socket, both unix and network
|
|
|
|
SocketType = iota
|
|
|
|
)
|
|
|
|
|
|
|
|
// INode is an entry in the filesystem. It can be:
|
|
|
|
// - a file
|
|
|
|
// - a directory
|
|
|
|
// - a symbolic link
|
|
|
|
// - a hard link
|
|
|
|
// - a char device/named pipe/socket
|
|
|
|
// It takes a Kind (as above described), a Size and a Path
|
|
|
|
type INode struct {
|
|
|
|
Kind int
|
|
|
|
Size int64
|
|
|
|
Path string
|
|
|
|
Children *INodeList
|
|
|
|
}
|
|
|
|
|
|
|
|
// INodeList is a simple *INode slice with a Mutex to allow safe
|
|
|
|
// concurrent write.
|
|
|
|
type INodeList struct {
|
|
|
|
Elements []*INode
|
|
|
|
safe sync.Mutex
|
|
|
|
}
|
|
|
|
|
2019-05-16 21:00:40 +02:00
|
|
|
type StatusReport struct {
|
2019-03-06 15:03:21 +01:00
|
|
|
path string
|
2019-05-16 21:00:40 +02:00
|
|
|
size int64
|
2019-03-06 15:03:21 +01:00
|
|
|
err error
|
|
|
|
}
|
|
|
|
|
|
|
|
// Append appends safely an inode to the INodeList.
|
|
|
|
func (il *INodeList) Append(node *INode) {
|
|
|
|
il.safe.Lock()
|
|
|
|
il.Elements = append(il.Elements, node)
|
|
|
|
il.safe.Unlock()
|
|
|
|
}
|
|
|
|
|
|
|
|
// INodeChan is a channel global to the sizer package that is used
|
2019-05-16 20:59:19 +02:00
|
|
|
// to funnel the retrieved sizes and infos on the inodes.
|
2019-03-06 15:03:21 +01:00
|
|
|
var INodeChan chan *INode
|
|
|
|
|
|
|
|
// ErrNotIdenfiedType is the error returned when the a classifier does not
|
2019-05-16 20:59:19 +02:00
|
|
|
// recognize the INode type.
|
2019-03-06 15:03:21 +01:00
|
|
|
var ErrNotIdenfiedType = errors.New("inode not identified")
|
|
|
|
|
|
|
|
// NewINode returns a new *INode with kind, size and path
|
|
|
|
func NewINode(kind int, size int64, path string) *INode {
|
|
|
|
var children INodeList
|
|
|
|
abspath, _ := filepath.Abs(path)
|
|
|
|
var newINode = INode{
|
|
|
|
Kind: kind,
|
|
|
|
Size: size,
|
|
|
|
Path: abspath,
|
|
|
|
Children: &children,
|
|
|
|
}
|
|
|
|
return &newINode
|
|
|
|
}
|
|
|
|
|
|
|
|
// Name returns the name of the file at the INode.
|
|
|
|
func (i *INode) Name() string {
|
|
|
|
_, name := filepath.Split(i.Path)
|
|
|
|
return name
|
|
|
|
}
|
|
|
|
|
2019-05-16 21:00:04 +02:00
|
|
|
// IdentifyType classifies a file-like object in one of the FileType.
|
|
|
|
func IdentifyType(path string) (int, int64, error) {
|
|
|
|
f, err := os.Lstat(path)
|
|
|
|
if err != nil {
|
|
|
|
return -1, 0, err
|
|
|
|
}
|
|
|
|
switch mode := f.Mode(); {
|
|
|
|
case mode.IsDir():
|
|
|
|
return DirType, f.Size(), nil
|
|
|
|
case mode.IsRegular():
|
|
|
|
return FileType, f.Size(), nil
|
|
|
|
}
|
|
|
|
return -1, 0, ErrNotIdenfiedType
|
2019-03-06 15:03:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func ls(path string) ([]string, error) {
|
|
|
|
var content []string
|
|
|
|
f, err := os.Open(path)
|
|
|
|
if err != nil {
|
|
|
|
return []string{}, err
|
|
|
|
}
|
|
|
|
files, err := f.Readdir(-1)
|
|
|
|
if err != nil {
|
|
|
|
return []string{}, err
|
|
|
|
}
|
|
|
|
for _, fi := range files {
|
|
|
|
content = append(content, filepath.Join(path, fi.Name()))
|
|
|
|
}
|
|
|
|
return content, nil
|
|
|
|
}
|
|
|
|
|
2019-05-16 21:02:54 +02:00
|
|
|
func INodeFromPath(path string) (*INode, error) {
|
|
|
|
kind, _, err := IdentifyType(path)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2019-03-06 15:03:21 +01:00
|
|
|
}
|
2019-05-16 21:02:54 +02:00
|
|
|
i := NewINode(kind, 0, path)
|
|
|
|
return i, nil
|
2019-03-06 15:03:21 +01:00
|
|
|
}
|
|
|
|
|
2019-05-16 21:02:26 +02:00
|
|
|
func (i *INode) size(wg *sync.WaitGroup) {
|
|
|
|
//Console.Debugln(i.Path)
|
|
|
|
i.Children.safe.Lock()
|
|
|
|
defer i.Children.safe.Unlock()
|
|
|
|
var wgIn sync.WaitGroup
|
|
|
|
for _, child := range i.Children.Elements {
|
|
|
|
wg.Add(1)
|
|
|
|
go child.walk(&wgIn)
|
2019-03-06 15:03:21 +01:00
|
|
|
}
|
2019-05-16 21:02:26 +02:00
|
|
|
wgIn.Wait()
|
|
|
|
for _, child := range i.Children.Elements {
|
|
|
|
i.Size += child.Size
|
|
|
|
wg.Done()
|
2019-03-06 15:03:21 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-16 21:02:26 +02:00
|
|
|
func (i *INode) walk(wg *sync.WaitGroup) {
|
|
|
|
children, err := ls(i.Path)
|
|
|
|
if err != nil {
|
|
|
|
Console.Println(err)
|
|
|
|
wg.Done()
|
2019-03-06 15:03:21 +01:00
|
|
|
return
|
|
|
|
}
|
2019-05-16 21:02:26 +02:00
|
|
|
for _, child := range children {
|
|
|
|
//Console.Debugln("Parent:", i.Path, "- Child:", child)
|
|
|
|
kind, size, err := IdentifyType(child)
|
|
|
|
if err != nil {
|
|
|
|
Console.Println(child, ":", err)
|
|
|
|
}
|
|
|
|
childINode := NewINode(kind, size, child)
|
|
|
|
i.Children.Append(childINode)
|
|
|
|
switch {
|
|
|
|
case kind == DirType:
|
|
|
|
//Console.Debugln("Size on dir:", child)
|
|
|
|
wg.Add(1)
|
|
|
|
childINode.size(wg)
|
|
|
|
case kind == FileType:
|
|
|
|
Console.Debugln("Filesize:", child, "-", size)
|
|
|
|
childINode.Size = size
|
|
|
|
default:
|
|
|
|
//Console.Debugln("Unknown:", child)
|
|
|
|
childINode.Size = 0
|
2019-03-06 15:03:21 +01:00
|
|
|
}
|
|
|
|
}
|
2019-05-16 21:02:26 +02:00
|
|
|
wg.Done()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (i *INode) Sizer() {
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
wg.Add(1)
|
|
|
|
i.walk(&wg)
|
|
|
|
wg.Wait()
|
|
|
|
Console.Println(i.Size)
|
2019-03-06 15:03:21 +01:00
|
|
|
}
|