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