Port envd from e2b with internalized shared packages and Connect RPC
- 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
This commit is contained in:
184
envd/internal/services/filesystem/dir.go
Normal file
184
envd/internal/services/filesystem/dir.go
Normal file
@ -0,0 +1,184 @@
|
||||
package filesystem
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"connectrpc.com/connect"
|
||||
|
||||
"git.omukk.dev/wrenn/sandbox/envd/internal/permissions"
|
||||
rpc "git.omukk.dev/wrenn/sandbox/envd/internal/services/spec/filesystem"
|
||||
)
|
||||
|
||||
func (s Service) ListDir(ctx context.Context, req *connect.Request[rpc.ListDirRequest]) (*connect.Response[rpc.ListDirResponse], error) {
|
||||
depth := req.Msg.GetDepth()
|
||||
if depth == 0 {
|
||||
depth = 1 // default depth to current directory
|
||||
}
|
||||
|
||||
u, err := permissions.GetAuthUser(ctx, s.defaults.User)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
requestedPath := req.Msg.GetPath()
|
||||
|
||||
// Expand the path so we can return absolute paths in the response.
|
||||
requestedPath, err = permissions.ExpandAndResolve(requestedPath, u, s.defaults.Workdir)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodeInvalidArgument, err)
|
||||
}
|
||||
|
||||
resolvedPath, err := followSymlink(requestedPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = checkIfDirectory(resolvedPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
entries, err := walkDir(requestedPath, resolvedPath, int(depth))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return connect.NewResponse(&rpc.ListDirResponse{
|
||||
Entries: entries,
|
||||
}), nil
|
||||
}
|
||||
|
||||
func (s Service) MakeDir(ctx context.Context, req *connect.Request[rpc.MakeDirRequest]) (*connect.Response[rpc.MakeDirResponse], error) {
|
||||
u, err := permissions.GetAuthUser(ctx, s.defaults.User)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dirPath, err := permissions.ExpandAndResolve(req.Msg.GetPath(), u, s.defaults.Workdir)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodeInvalidArgument, err)
|
||||
}
|
||||
|
||||
stat, err := os.Stat(dirPath)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return nil, connect.NewError(connect.CodeInternal, fmt.Errorf("error getting file info: %w", err))
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
if stat.IsDir() {
|
||||
return nil, connect.NewError(connect.CodeAlreadyExists, fmt.Errorf("directory already exists: %s", dirPath))
|
||||
}
|
||||
|
||||
return nil, connect.NewError(connect.CodeInvalidArgument, fmt.Errorf("path already exists but it is not a directory: %s", dirPath))
|
||||
}
|
||||
|
||||
uid, gid, userErr := permissions.GetUserIdInts(u)
|
||||
if userErr != nil {
|
||||
return nil, connect.NewError(connect.CodeInternal, userErr)
|
||||
}
|
||||
|
||||
userErr = permissions.EnsureDirs(dirPath, uid, gid)
|
||||
if userErr != nil {
|
||||
return nil, connect.NewError(connect.CodeInternal, userErr)
|
||||
}
|
||||
|
||||
entry, err := entryInfo(dirPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return connect.NewResponse(&rpc.MakeDirResponse{
|
||||
Entry: entry,
|
||||
}), nil
|
||||
}
|
||||
|
||||
// followSymlink resolves a symbolic link to its target path.
|
||||
func followSymlink(path string) (string, error) {
|
||||
// Resolve symlinks
|
||||
resolvedPath, err := filepath.EvalSymlinks(path)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return "", connect.NewError(connect.CodeNotFound, fmt.Errorf("path not found: %w", err))
|
||||
}
|
||||
|
||||
if strings.Contains(err.Error(), "too many links") {
|
||||
return "", connect.NewError(connect.CodeFailedPrecondition, fmt.Errorf("cyclic symlink or chain >255 links at %q", path))
|
||||
}
|
||||
|
||||
return "", connect.NewError(connect.CodeInternal, fmt.Errorf("error resolving symlink: %w", err))
|
||||
}
|
||||
|
||||
return resolvedPath, nil
|
||||
}
|
||||
|
||||
// checkIfDirectory checks if the given path is a directory.
|
||||
func checkIfDirectory(path string) error {
|
||||
stat, err := os.Stat(path)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return connect.NewError(connect.CodeNotFound, fmt.Errorf("directory not found: %w", err))
|
||||
}
|
||||
|
||||
return connect.NewError(connect.CodeInternal, fmt.Errorf("error getting file info: %w", err))
|
||||
}
|
||||
|
||||
if !stat.IsDir() {
|
||||
return connect.NewError(connect.CodeInvalidArgument, fmt.Errorf("path is not a directory: %s", path))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// walkDir walks the directory tree starting from dirPath up to the specified depth (doesn't follow symlinks).
|
||||
func walkDir(requestedPath string, dirPath string, depth int) (entries []*rpc.EntryInfo, err error) {
|
||||
err = filepath.WalkDir(dirPath, func(path string, _ os.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Skip the root directory itself
|
||||
if path == dirPath {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Calculate current depth
|
||||
relPath, err := filepath.Rel(dirPath, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
currentDepth := len(strings.Split(relPath, string(os.PathSeparator)))
|
||||
|
||||
if currentDepth > depth {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
|
||||
entryInfo, err := entryInfo(path)
|
||||
if err != nil {
|
||||
var connectErr *connect.Error
|
||||
if errors.As(err, &connectErr) && connectErr.Code() == connect.CodeNotFound {
|
||||
// Skip entries that don't exist anymore
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Return the requested path as the base path instead of the symlink-resolved path
|
||||
path = filepath.Join(requestedPath, relPath)
|
||||
entryInfo.Path = path
|
||||
|
||||
entries = append(entries, entryInfo)
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodeInternal, fmt.Errorf("error reading directory %s: %w", dirPath, err))
|
||||
}
|
||||
|
||||
return entries, nil
|
||||
}
|
||||
Reference in New Issue
Block a user