misc: add GetFiles and supporting functions
This commit is contained in:
163
internal/misc/getfiles.go
Normal file
163
internal/misc/getfiles.go
Normal file
@@ -0,0 +1,163 @@
|
||||
package misc
|
||||
|
||||
import (
|
||||
"debug/elf"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
func GetFiles(list []string, required bool) (files []string, err error) {
|
||||
for _, file := range list {
|
||||
filelist, err := getFile(file, required)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
files = append(files, filelist...)
|
||||
}
|
||||
|
||||
files = RemoveDuplicates(files)
|
||||
return
|
||||
}
|
||||
|
||||
func getFile(file string, required bool) (files []string, err error) {
|
||||
// Expand glob expression
|
||||
expanded, err := filepath.Glob(file)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if len(expanded) > 0 && expanded[0] != file {
|
||||
for _, path := range expanded {
|
||||
if globFiles, err := getFile(path, required); err != nil {
|
||||
return files, err
|
||||
} else {
|
||||
files = append(files, globFiles...)
|
||||
}
|
||||
}
|
||||
return RemoveDuplicates(files), nil
|
||||
}
|
||||
|
||||
fileInfo, err := os.Stat(file)
|
||||
if err != nil {
|
||||
if required {
|
||||
return files, fmt.Errorf("getFile: failed to stat file %q: %w", file, err)
|
||||
}
|
||||
return files, nil
|
||||
}
|
||||
|
||||
if fileInfo.IsDir() {
|
||||
// Recurse over directory contents
|
||||
err := filepath.Walk(file, func(path string, f os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if f.IsDir() {
|
||||
return nil
|
||||
}
|
||||
newFiles, err := getFile(path, required)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
files = append(files, newFiles...)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return files, err
|
||||
}
|
||||
} else {
|
||||
files = append(files, file)
|
||||
|
||||
// get dependencies for binaries
|
||||
if _, err := elf.Open(file); err == nil {
|
||||
if binaryDepFiles, err := getBinaryDeps(file); err != nil {
|
||||
return files, err
|
||||
} else {
|
||||
files = append(files, binaryDepFiles...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
files = RemoveDuplicates(files)
|
||||
return
|
||||
}
|
||||
|
||||
func getDeps(file string, parents map[string]struct{}) (files []string, err error) {
|
||||
|
||||
if _, found := parents[file]; found {
|
||||
return
|
||||
}
|
||||
|
||||
// get dependencies for binaries
|
||||
fd, err := elf.Open(file)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getDeps: unable to open elf binary %q: %w", file, err)
|
||||
}
|
||||
libs, _ := fd.ImportedLibraries()
|
||||
fd.Close()
|
||||
files = append(files, file)
|
||||
parents[file] = struct{}{}
|
||||
|
||||
if len(libs) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// we don't recursively search these paths for performance reasons
|
||||
libdirGlobs := []string{
|
||||
"/usr/lib",
|
||||
"/lib",
|
||||
"/usr/lib/expect*",
|
||||
}
|
||||
|
||||
for _, lib := range libs {
|
||||
found := false
|
||||
findDepLoop:
|
||||
for _, libdirGlob := range libdirGlobs {
|
||||
libdirs, _ := filepath.Glob(libdirGlob)
|
||||
for _, libdir := range libdirs {
|
||||
path := filepath.Join(libdir, lib)
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
binaryDepFiles, err := getDeps(path, parents)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
files = append(files, binaryDepFiles...)
|
||||
files = append(files, path)
|
||||
found = true
|
||||
break findDepLoop
|
||||
}
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return nil, fmt.Errorf("getDeps: unable to locate dependency for %q: %s", file, lib)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Recursively list all dependencies for a given ELF binary
|
||||
func getBinaryDeps(file string) ([]string, error) {
|
||||
// if file is a symlink, resolve dependencies for target
|
||||
fileStat, err := os.Lstat(file)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getBinaryDeps: failed to stat file %q: %w", file, err)
|
||||
}
|
||||
|
||||
// Symlink: write symlink to archive then set 'file' to link target
|
||||
if fileStat.Mode()&os.ModeSymlink != 0 {
|
||||
target, err := os.Readlink(file)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getBinaryDeps: unable to read symlink %q: %w", file, err)
|
||||
}
|
||||
if !filepath.IsAbs(target) {
|
||||
target, err = RelativeSymlinkTargetToDir(target, filepath.Dir(file))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
file = target
|
||||
}
|
||||
|
||||
return getDeps(file, make(map[string]struct{}))
|
||||
|
||||
}
|
Reference in New Issue
Block a user