-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
merge #34 into openSUSE/libpathrs:main
Aleksa Sarai (9): Root::create: use mknod for regular files as well capi: switch to fd-based API Root::create_file: take open flags to configure returned fd go bindings: completely rewrite Go bindings to new API examples: go: switch to modules python bindings: update bindings to use new API examples: python/static_web: handle non-directories capi: remove backtraces from FFI API capi: correctly mark Leakable methods as unsafe LGTMs: cyphar
- Loading branch information
Showing
30 changed files
with
1,498 additions
and
2,485 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
//go:build linux | ||
|
||
// libpathrs: safe path resolution on Linux | ||
// Copyright (C) 2019-2024 Aleksa Sarai <[email protected]> | ||
// Copyright (C) 2019-2024 SUSE LLC | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
// Package pathrs provides bindings for libpathrs, a library for safe path | ||
// resolution on Linux. | ||
package pathrs |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
//go:build linux | ||
|
||
// libpathrs: safe path resolution on Linux | ||
// Copyright (C) 2019-2024 Aleksa Sarai <[email protected]> | ||
// Copyright (C) 2019-2024 SUSE LLC | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package pathrs | ||
|
||
import ( | ||
"syscall" | ||
) | ||
|
||
// Error represents an underlying libpathrs error. | ||
type Error struct { | ||
description string | ||
errno syscall.Errno | ||
} | ||
|
||
// Error returns a textual description of the error. | ||
func (err *Error) Error() string { | ||
return err.description | ||
} | ||
|
||
// Unwrap returns the underlying error which was wrapped by this error (if | ||
// applicable). | ||
func (err *Error) Unwrap() error { | ||
if err.errno != 0 { | ||
return err.errno | ||
} | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
module github.com/openSUSE/libpathrs/go-pathrs | ||
|
||
go 1.18 | ||
|
||
require golang.org/x/sys v0.22.0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= | ||
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
//go:build linux | ||
|
||
// libpathrs: safe path resolution on Linux | ||
// Copyright (C) 2019-2024 Aleksa Sarai <[email protected]> | ||
// Copyright (C) 2019-2024 SUSE LLC | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package pathrs | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
) | ||
|
||
// Handle is a handle for a path within a given Root. This handle references an | ||
// already-resolved path which can be used for only one purpose -- to "re-open" | ||
// the handle and get an actual *os.File which can be used for ordinary | ||
// operations. | ||
// | ||
// It is critical that perform all relevant operations through this Handle | ||
// (rather than fetching the file descriptor yourself with IntoRaw), because | ||
// the security properties of libpathrs depend on users doing all relevant | ||
// filesystem operations through libpathrs. | ||
type Handle struct { | ||
inner *os.File | ||
} | ||
|
||
// HandleFromFile creates a new Handle from an exisitng file handle. The handle | ||
// will be copied by this method, so the original handle should still be freed | ||
// by the caller. | ||
// | ||
// This is effectively the inverse operation of Handle.IntoRaw, and is used for | ||
// "deserialising" pathrs root handles. | ||
func HandleFromFile(file *os.File) (*Handle, error) { | ||
newFile, err := dupFile(file) | ||
if err != nil { | ||
return nil, fmt.Errorf("duplicate handle fd: %w", err) | ||
} | ||
return &Handle{inner: newFile}, nil | ||
} | ||
|
||
// Open creates an "upgraded" file handle to the file referenced by the Handle. | ||
// Note that the original Handle is not consumed by this operation, and can be | ||
// opened multiple times. | ||
// | ||
// The handle returned is only usable for reading, and this is method is | ||
// shorthand for handle.OpenFile(os.O_RDONLY). | ||
// | ||
// TODO: Rename these to "Reopen" or something. | ||
func (h *Handle) Open() (*os.File, error) { | ||
return h.OpenFile(os.O_RDONLY) | ||
} | ||
|
||
// OpenFile creates an "upgraded" file handle to the file referenced by the | ||
// Handle. Note that the original Handle is not consumed by this operation, and | ||
// can be opened multiple times. | ||
// | ||
// The provided flags indicate which open(2) flags are used to create the new | ||
// handle. | ||
// | ||
// TODO: Rename these to "Reopen" or something. | ||
func (h *Handle) OpenFile(flags int) (*os.File, error) { | ||
return withFileFd(h.inner, func(fd uintptr) (*os.File, error) { | ||
newFd, err := pathrsReopen(fd, flags) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return os.NewFile(uintptr(newFd), h.inner.Name()), nil | ||
}) | ||
} | ||
|
||
// IntoFile unwraps the Handle into its underlying *os.File. | ||
// | ||
// You almost certainly want to use OpenFile() to get a non-O_PATH version of | ||
// this Handle. | ||
// | ||
// This operation returns the internal *os.File of the Handle directly, so | ||
// Close()ing the Handle will also close any copies of the returned *os.File. | ||
// If you want to get an independent copy, use Clone().IntoFile(). | ||
func (h *Handle) IntoFile() *os.File { | ||
// TODO: Figure out if we really don't want to make a copy. | ||
// TODO: We almost certainly want to clear r.inner here, but we can't do | ||
// that easily atomically (we could use atomic.Value but that'll make | ||
// things quite a bit uglier). | ||
return h.inner | ||
} | ||
|
||
// Clone creates a copy of a Handle, such that it has a separate lifetime to | ||
// the original (while refering to the same underlying file). | ||
func (h *Handle) Clone() (*Handle, error) { | ||
return HandleFromFile(h.inner) | ||
} | ||
|
||
// Close frees all of the resources used by the Handle. | ||
func (h *Handle) Close() error { | ||
return h.inner.Close() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
//go:build linux | ||
|
||
// libpathrs: safe path resolution on Linux | ||
// Copyright (C) 2019-2024 Aleksa Sarai <[email protected]> | ||
// Copyright (C) 2019-2024 SUSE LLC | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package pathrs | ||
|
||
import ( | ||
"syscall" | ||
"unsafe" | ||
) | ||
|
||
// TODO: Switch to pkg-config. | ||
|
||
// #cgo CFLAGS: -I${SRCDIR}/../../../include | ||
// #cgo LDFLAGS: -L${SRCDIR}/../../../target/release -L${SRCDIR}/../../../target/debug -lpathrs | ||
// #include <pathrs.h> | ||
import "C" | ||
|
||
func fetchError(errId C.int) error { | ||
if errId >= 0 { | ||
return nil | ||
} | ||
cErr := C.pathrs_errorinfo(errId) | ||
defer C.pathrs_errorinfo_free(cErr) | ||
|
||
var err error | ||
if cErr != nil { | ||
err = &Error{ | ||
errno: syscall.Errno(cErr.saved_errno), | ||
description: C.GoString(cErr.description), | ||
} | ||
} | ||
return err | ||
} | ||
|
||
func pathrsOpen(path string) (uintptr, error) { | ||
cPath := C.CString(path) | ||
defer C.free(unsafe.Pointer(cPath)) | ||
|
||
fd := C.pathrs_root_open(cPath) | ||
return uintptr(fd), fetchError(fd) | ||
} | ||
|
||
func pathrsReopen(fd uintptr, flags int) (uintptr, error) { | ||
newFd := C.pathrs_reopen(C.int(fd), C.int(flags)) | ||
return uintptr(newFd), fetchError(newFd) | ||
} | ||
|
||
func pathrsResolve(rootFd uintptr, path string) (uintptr, error) { | ||
cPath := C.CString(path) | ||
defer C.free(unsafe.Pointer(cPath)) | ||
|
||
fd := C.pathrs_resolve(C.int(rootFd), cPath) | ||
return uintptr(fd), fetchError(fd) | ||
} | ||
|
||
func pathrsCreat(rootFd uintptr, path string, flags int, mode uint32) (uintptr, error) { | ||
cPath := C.CString(path) | ||
defer C.free(unsafe.Pointer(cPath)) | ||
|
||
fd := C.pathrs_creat(C.int(rootFd), cPath, C.int(flags), C.uint(mode)) | ||
return uintptr(fd), fetchError(fd) | ||
} | ||
|
||
func pathrsRename(rootFd uintptr, src, dst string, flags uint) error { | ||
cSrc := C.CString(src) | ||
defer C.free(unsafe.Pointer(cSrc)) | ||
|
||
cDst := C.CString(dst) | ||
defer C.free(unsafe.Pointer(cDst)) | ||
|
||
err := C.pathrs_rename(C.int(rootFd), cSrc, cDst, C.uint(flags)) | ||
return fetchError(err) | ||
} | ||
|
||
func pathrsMkdir(rootFd uintptr, path string, mode uint32) error { | ||
cPath := C.CString(path) | ||
defer C.free(unsafe.Pointer(cPath)) | ||
|
||
err := C.pathrs_mkdir(C.int(rootFd), cPath, C.uint(mode)) | ||
return fetchError(err) | ||
} | ||
|
||
func pathrsMknod(rootFd uintptr, path string, mode uint32, dev uint64) error { | ||
cPath := C.CString(path) | ||
defer C.free(unsafe.Pointer(cPath)) | ||
|
||
err := C.pathrs_mknod(C.int(rootFd), cPath, C.uint(mode), C.dev_t(dev)) | ||
return fetchError(err) | ||
} | ||
|
||
func pathrsSymlink(rootFd uintptr, path, target string) error { | ||
cPath := C.CString(path) | ||
defer C.free(unsafe.Pointer(cPath)) | ||
|
||
cTarget := C.CString(target) | ||
defer C.free(unsafe.Pointer(cTarget)) | ||
|
||
err := C.pathrs_symlink(C.int(rootFd), cPath, cTarget) | ||
return fetchError(err) | ||
} | ||
|
||
func pathrsHardlink(rootFd uintptr, path, target string) error { | ||
cPath := C.CString(path) | ||
defer C.free(unsafe.Pointer(cPath)) | ||
|
||
cTarget := C.CString(target) | ||
defer C.free(unsafe.Pointer(cTarget)) | ||
|
||
err := C.pathrs_hardlink(C.int(rootFd), cPath, cTarget) | ||
return fetchError(err) | ||
} |
Oops, something went wrong.