Skip to content

Commit

Permalink
go binding: use pathrs_proc_readlink to get *os.File names
Browse files Browse the repository at this point in the history
Users shouldn't use (*os.File).Name() but a lot of Go users do use it
for debugging and logging purposes, so it's best to include the real
name of the file.

(We might want to add some prefix to the name in the future to indicate
that this name is for informational purposes only, and shouldn't be used
for actual filesystem operations.)

Signed-off-by: Aleksa Sarai <cyphar@cyphar.com>
  • Loading branch information
cyphar committed Jul 31, 2024
1 parent 0f4b8cc commit 6145ea4
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 48 deletions.
2 changes: 2 additions & 0 deletions examples/go/cat.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ func main() {
}
defer file.Close()

fmt.Fprintf(os.Stderr, "file %q (from root %q):\n", file.Name(), root.IntoFile().Name())

_, err = io.Copy(os.Stdout, file)
if err != nil {
fmt.Printf("Cannot write content of file to stdout, %v\n", err)
Expand Down
45 changes: 16 additions & 29 deletions go-pathrs/root_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,10 @@ func OpenRoot(path string) (*Root, error) {
if err != nil {
return nil, err
}
file := os.NewFile(fd, "//pathrs-root:"+path)
file, err := mkFile(fd)
if err != nil {
return nil, err
}
return &Root{inner: file}, nil
}

Expand All @@ -67,20 +70,15 @@ func RootFromFile(file *os.File) (*Root, error) {
// resolved within the rootfs. If you wish to open a handle to the symlink
// itself, use ResolveNoFollow.
func (r *Root) Resolve(path string) (*Handle, error) {
// TODO: Get the actual name of the handle through /proc/self/fd/...
fakeName, err := randName(32)
if err != nil {
return nil, err
}
// Prefix the root.
fakeName = r.inner.Name() + fakeName

return withFileFd(r.inner, func(rootFd uintptr) (*Handle, error) {
handleFd, err := pathrsResolve(rootFd, path)
if err != nil {
return nil, err
}
handleFile := os.NewFile(uintptr(handleFd), fakeName)
handleFile, err := mkFile(uintptr(handleFd))
if err != nil {
return nil, err
}
return &Handle{inner: handleFile}, nil
})
}
Expand All @@ -90,20 +88,15 @@ func (r *Root) Resolve(path string) (*Handle, error) {
// followed. If the final component is a trailing symlink, an O_PATH|O_NOFOLLOW
// handle to the symlink itself is returned.
func (r *Root) ResolveNoFollow(path string) (*Handle, error) {
// TODO: Get the actual name of the handle through /proc/self/fd/...
fakeName, err := randName(32)
if err != nil {
return nil, err
}
// Prefix the root.
fakeName = r.inner.Name() + fakeName

return withFileFd(r.inner, func(rootFd uintptr) (*Handle, error) {
handleFd, err := pathrsResolveNoFollow(rootFd, path)
if err != nil {
return nil, err
}
handleFile := os.NewFile(uintptr(handleFd), fakeName)
handleFile, err := mkFile(uintptr(handleFd))
if err != nil {
return nil, err
}
return &Handle{inner: handleFile}, nil
})
}
Expand All @@ -112,25 +105,19 @@ func (r *Root) ResolveNoFollow(path string) (*Handle, error) {
// and returns a handle to the file. The provided mode is used for the new file
// (the process's umask applies).
func (r *Root) Create(path string, flags int, mode os.FileMode) (*Handle, error) {
// TODO: Get the actual name of the handle through /proc/self/fd/...
fakeName, err := randName(32)
if err != nil {
return nil, err
}
// Prefix the root.
fakeName = r.inner.Name() + fakeName

unixMode, err := toUnixMode(mode)
if err != nil {
return nil, err
}

return withFileFd(r.inner, func(rootFd uintptr) (*Handle, error) {
handleFd, err := pathrsCreat(rootFd, path, flags, unixMode)
if err != nil {
return nil, err
}
handleFile := os.NewFile(uintptr(handleFd), fakeName)
handleFile, err := mkFile(uintptr(handleFd))
if err != nil {
return nil, err
}
return &Handle{inner: handleFile}, nil
})
}
Expand Down
32 changes: 13 additions & 19 deletions go-pathrs/utils_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,8 @@
package pathrs

import (
"crypto/rand"
"fmt"
"os"
"strings"

"golang.org/x/sys/unix"
)
Expand Down Expand Up @@ -93,22 +91,18 @@ func dupFile(file *os.File) (*os.File, error) {
})
}

// randName generates a random hexadecimal name that is used for the Go-level
// "file name" of libpathrs-generated fds, and can be used to help with
// debugging.
func randName(k int) (string, error) {
randBuf := make([]byte, k/2)

if n, err := rand.Read(randBuf); err != nil {
return "", err
} else if n != len(randBuf) {
return "", fmt.Errorf("rand.Read didn't return enough bytes (%d != %d)", n, len(randBuf))
}

var nameBuf strings.Builder
nameBuf.WriteString("//pathrs-fd:")
for _, b := range randBuf {
nameBuf.WriteString(fmt.Sprintf("%.2x", b))
// mkFile creates a new *os.File from the provided file descriptor. However,
// unlike os.NewFile, the file's Name is based on the real path (provided by
// /proc/self/fd/$n).
func mkFile(fd uintptr) (*os.File, error) {
fdPath := fmt.Sprintf("fd/%d", fd)
fdName, err := ProcReadlink(ProcBaseThreadSelf, fdPath)
if err != nil {
_ = unix.Close(int(fd))
return nil, fmt.Errorf("failed to fetch real name of fd %d: %w", fd, err)
}
return nameBuf.String(), nil
// TODO: Maybe we should prefix this name with something to indicate to
// users that they must not use this path as a "safe" path. Something like
// "//pathrs-handle:/foo/bar"?
return os.NewFile(fd, fdName), nil
}

0 comments on commit 6145ea4

Please sign in to comment.