- Copy envd source from e2b-dev/infra, internalize shared dependencies
into envd/internal/shared/ (keys, filesystem, id, smap, utils)
- Switch from gRPC to Connect RPC for all envd services
- Update module paths to git.omukk.dev/wrenn/{sandbox,sandbox/envd}
- Add proto specs (process, filesystem) with buf-based code generation
- Implement full envd: process exec, filesystem ops, port forwarding,
cgroup management, MMDS integration, and HTTP API
- Update main module dependencies (firecracker SDK, pgx, goose, etc.)
- Remove placeholder .gitkeep files replaced by real implementations
108 lines
2.9 KiB
Go
108 lines
2.9 KiB
Go
package filesystem
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"os/user"
|
|
"syscall"
|
|
"time"
|
|
|
|
"connectrpc.com/connect"
|
|
"google.golang.org/protobuf/types/known/timestamppb"
|
|
|
|
rpc "git.omukk.dev/wrenn/sandbox/envd/internal/services/spec/filesystem"
|
|
"git.omukk.dev/wrenn/sandbox/envd/internal/shared/filesystem"
|
|
)
|
|
|
|
// Filesystem magic numbers from Linux kernel (include/uapi/linux/magic.h)
|
|
const (
|
|
nfsSuperMagic = 0x6969
|
|
cifsMagic = 0xFF534D42
|
|
smbSuperMagic = 0x517B
|
|
smb2MagicNumber = 0xFE534D42
|
|
fuseSuperMagic = 0x65735546
|
|
)
|
|
|
|
// IsPathOnNetworkMount checks if the given path is on a network filesystem mount.
|
|
// Returns true if the path is on NFS, CIFS, SMB, or FUSE filesystem.
|
|
func IsPathOnNetworkMount(path string) (bool, error) {
|
|
var statfs syscall.Statfs_t
|
|
if err := syscall.Statfs(path, &statfs); err != nil {
|
|
return false, fmt.Errorf("failed to statfs %s: %w", path, err)
|
|
}
|
|
|
|
switch statfs.Type {
|
|
case nfsSuperMagic, cifsMagic, smbSuperMagic, smb2MagicNumber, fuseSuperMagic:
|
|
return true, nil
|
|
default:
|
|
return false, nil
|
|
}
|
|
}
|
|
|
|
func entryInfo(path string) (*rpc.EntryInfo, error) {
|
|
info, err := filesystem.GetEntryFromPath(path)
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
return nil, connect.NewError(connect.CodeNotFound, fmt.Errorf("file not found: %w", err))
|
|
}
|
|
|
|
return nil, connect.NewError(connect.CodeInternal, fmt.Errorf("error getting file info: %w", err))
|
|
}
|
|
|
|
owner, group := getFileOwnership(info)
|
|
|
|
return &rpc.EntryInfo{
|
|
Name: info.Name,
|
|
Type: getEntryType(info.Type),
|
|
Path: info.Path,
|
|
Size: info.Size,
|
|
Mode: uint32(info.Mode),
|
|
Permissions: info.Permissions,
|
|
Owner: owner,
|
|
Group: group,
|
|
ModifiedTime: toTimestamp(info.ModifiedTime),
|
|
SymlinkTarget: info.SymlinkTarget,
|
|
}, nil
|
|
}
|
|
|
|
func toTimestamp(time time.Time) *timestamppb.Timestamp {
|
|
if time.IsZero() {
|
|
return nil
|
|
}
|
|
|
|
return timestamppb.New(time)
|
|
}
|
|
|
|
// getFileOwnership returns the owner and group names for a file.
|
|
// If the lookup fails, it returns the numeric UID and GID as strings.
|
|
func getFileOwnership(fileInfo filesystem.EntryInfo) (owner, group string) {
|
|
// Look up username
|
|
owner = fmt.Sprintf("%d", fileInfo.UID)
|
|
if u, err := user.LookupId(owner); err == nil {
|
|
owner = u.Username
|
|
}
|
|
|
|
// Look up group name
|
|
group = fmt.Sprintf("%d", fileInfo.GID)
|
|
if g, err := user.LookupGroupId(group); err == nil {
|
|
group = g.Name
|
|
}
|
|
|
|
return owner, group
|
|
}
|
|
|
|
// 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(fileType filesystem.FileType) rpc.FileType {
|
|
switch fileType {
|
|
case filesystem.FileFileType:
|
|
return rpc.FileType_FILE_TYPE_FILE
|
|
case filesystem.DirectoryFileType:
|
|
return rpc.FileType_FILE_TYPE_DIRECTORY
|
|
case filesystem.SymlinkFileType:
|
|
return rpc.FileType_FILE_TYPE_SYMLINK
|
|
default:
|
|
return rpc.FileType_FILE_TYPE_UNSPECIFIED
|
|
}
|
|
}
|