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) }