ruspa.v0/sizer/main.go

181 lines
4 KiB
Go

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
}
type StatusReport struct {
path string
size int64
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
// to funnel the retrieved sizes and infos on the inodes.
var INodeChan chan *INode
// ErrNotIdenfiedType is the error returned when the a classifier does not
// recognize the INode type.
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
}
// 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
}
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
}
func INodeFromPath(path string) (*INode, error) {
kind, _, err := IdentifyType(path)
if err != nil {
return nil, err
}
i := NewINode(kind, 0, path)
return i, nil
}
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)
}
wgIn.Wait()
for _, child := range i.Children.Elements {
i.Size += child.Size
wg.Done()
}
}
func (i *INode) walk(wg *sync.WaitGroup) {
children, err := ls(i.Path)
if err != nil {
Console.Println(err)
wg.Done()
return
}
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
}
}
wg.Done()
}
func (i *INode) Sizer() {
var wg sync.WaitGroup
wg.Add(1)
i.walk(&wg)
wg.Wait()
Console.Println(i.Size)
}