65 lines
1.7 KiB
Go
65 lines
1.7 KiB
Go
package fsutil
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"runtime"
|
|
|
|
"github.com/pkg/errors"
|
|
"github.com/tonistiigi/fsutil/types"
|
|
)
|
|
|
|
// constructs a Stat object. path is where the path can be found right
|
|
// now, relpath is the desired path to be recorded in the stat (so
|
|
// relative to whatever base dir is relevant). fi is the os.Stat
|
|
// info. inodemap is used to calculate hardlinks over a series of
|
|
// mkstat calls and maps inode to the canonical (aka "first") path for
|
|
// a set of hardlinks to that inode.
|
|
func mkstat(path, relpath string, fi os.FileInfo, inodemap map[uint64]string) (*types.Stat, error) {
|
|
relpath = filepath.ToSlash(relpath)
|
|
|
|
stat := &types.Stat{
|
|
Path: relpath,
|
|
Mode: uint32(fi.Mode()),
|
|
ModTime: fi.ModTime().UnixNano(),
|
|
}
|
|
|
|
setUnixOpt(fi, stat, relpath, inodemap)
|
|
|
|
if !fi.IsDir() {
|
|
stat.Size_ = fi.Size()
|
|
if fi.Mode()&os.ModeSymlink != 0 {
|
|
link, err := os.Readlink(path)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "failed to readlink %s", path)
|
|
}
|
|
stat.Linkname = link
|
|
}
|
|
}
|
|
if err := loadXattr(path, stat); err != nil {
|
|
return nil, errors.Wrapf(err, "failed to xattr %s", relpath)
|
|
}
|
|
|
|
if runtime.GOOS == "windows" {
|
|
permPart := stat.Mode & uint32(os.ModePerm)
|
|
noPermPart := stat.Mode &^ uint32(os.ModePerm)
|
|
// Add the x bit: make everything +x from windows
|
|
permPart |= 0111
|
|
permPart &= 0755
|
|
stat.Mode = noPermPart | permPart
|
|
}
|
|
|
|
// Clear the socket bit since archive/tar.FileInfoHeader does not handle it
|
|
stat.Mode &^= uint32(os.ModeSocket)
|
|
|
|
return stat, nil
|
|
}
|
|
|
|
func Stat(path string) (*types.Stat, error) {
|
|
fi, err := os.Lstat(path)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "os stat")
|
|
}
|
|
return mkstat(path, filepath.Base(path), fi, nil)
|
|
}
|