Files
sandbox/envd/internal/shared/filesystem/entry.go

111 lines
2.4 KiB
Go

// SPDX-License-Identifier: Apache-2.0
package filesystem
import (
"os"
"path/filepath"
"syscall"
"time"
)
func GetEntryFromPath(path string) (EntryInfo, error) {
fileInfo, err := os.Lstat(path)
if err != nil {
return EntryInfo{}, err
}
return GetEntryInfo(path, fileInfo), nil
}
func GetEntryInfo(path string, fileInfo os.FileInfo) EntryInfo {
fileMode := fileInfo.Mode()
var symlinkTarget *string
if fileMode&os.ModeSymlink != 0 {
// If we can't resolve the symlink target, we won't set the target
target := followSymlink(path)
symlinkTarget = &target
}
var entryType FileType
var mode os.FileMode
if symlinkTarget == nil {
entryType = getEntryType(fileMode)
mode = fileMode.Perm()
} else {
// If it's a symlink, we need to determine the type of the target
targetInfo, err := os.Stat(*symlinkTarget)
if err != nil {
entryType = UnknownFileType
} else {
entryType = getEntryType(targetInfo.Mode())
mode = targetInfo.Mode().Perm()
}
}
entry := EntryInfo{
Name: fileInfo.Name(),
Path: path,
Type: entryType,
Size: fileInfo.Size(),
Mode: mode,
Permissions: fileMode.String(),
ModifiedTime: fileInfo.ModTime(),
SymlinkTarget: symlinkTarget,
}
if base := getBase(fileInfo.Sys()); base != nil {
entry.AccessedTime = toTimestamp(base.Atim)
entry.CreatedTime = toTimestamp(base.Ctim)
entry.ModifiedTime = toTimestamp(base.Mtim)
entry.UID = base.Uid
entry.GID = base.Gid
} else if !fileInfo.ModTime().IsZero() {
entry.ModifiedTime = fileInfo.ModTime()
}
return entry
}
// getEntryType determines the type of file entry based on its mode and path.
// If the file is a symlink, it follows the symlink to determine the actual type.
func getEntryType(mode os.FileMode) FileType {
switch {
case mode.IsRegular():
return FileFileType
case mode.IsDir():
return DirectoryFileType
case mode&os.ModeSymlink == os.ModeSymlink:
return SymlinkFileType
default:
return UnknownFileType
}
}
// followSymlink resolves a symbolic link to its target path.
func followSymlink(path string) string {
// Resolve symlinks
resolvedPath, err := filepath.EvalSymlinks(path)
if err != nil {
return path
}
return resolvedPath
}
func toTimestamp(spec syscall.Timespec) time.Time {
if spec.Sec == 0 && spec.Nsec == 0 {
return time.Time{}
}
return time.Unix(spec.Sec, spec.Nsec)
}
func getBase(sys any) *syscall.Stat_t {
st, _ := sys.(*syscall.Stat_t)
return st
}