aboutsummaryrefslogtreecommitdiff
path: root/misc
diff options
context:
space:
mode:
authorRunxi Yu <me@runxiyu.org>2025-01-04 20:40:00 +0800
committerRunxi Yu <me@runxiyu.org>2025-01-04 20:41:42 +0800
commitb7dac177c81b1d1414a568c4375b24052045d4e6 (patch)
tree3e08decf0028ad8c87e656a9252972caf57ed5c2 /misc
parentAdd style conventions (diff)
downloadgo-lindenii-common-b7dac177c81b1d1414a568c4375b24052045d4e6.tar.gz
go-lindenii-common-b7dac177c81b1d1414a568c4375b24052045d4e6.tar.zst
go-lindenii-common-b7dac177c81b1d1414a568c4375b24052045d4e6.zip
misc: open_file
Diffstat (limited to 'misc')
-rw-r--r--misc/open_file.go75
-rw-r--r--misc/openat2.go4
2 files changed, 77 insertions, 2 deletions
diff --git a/misc/open_file.go b/misc/open_file.go
new file mode 100644
index 0000000..6767961
--- /dev/null
+++ b/misc/open_file.go
@@ -0,0 +1,75 @@
+package misc
+
+import (
+ "os"
+ "errors"
+ "strings"
+ "syscall"
+ "path"
+)
+
+type Dir_t struct {
+ fd int
+ name string
+}
+
+var (
+ Err_illegal_filename = errors.New("illegal filename")
+ Err_invalid_file_descriptor = errors.New("invalid file descriptor")
+)
+
+// Open a file at exactly the given directory.
+func Open_file_at(dir Dir_t, filename string, flags int, perms os.FileMode) (*os.File, error) {
+ if strings.IndexByte(filename, '/') != -1 {
+ return nil, Err_illegal_filename
+ }
+ return Open_file_beneath(dir, filename, flags, perms)
+}
+
+// Open a file at or beneath the given directory.
+func Open_file_beneath(dir Dir_t, filename string, flags int, perms os.FileMode) (*os.File, error) {
+ fd, err := Openat2(dir.fd, filename, &Open_how_t{
+ Flags: uint64(flags),
+ Mode: uint64(syscallMode(perms)),
+ Resolve: RESOLVE_BENEATH|RESOLVE_NO_SYMLINKS,
+ })
+ if err != nil {
+ return nil, err
+ }
+ file := os.NewFile(uintptr(fd), path.Join(dir.name, filename))
+ if file == nil {
+ return nil, Err_invalid_file_descriptor
+ } else {
+ return file, nil
+ }
+}
+
+// Open a directory as read-only and return a Dir_t to it. The caller is
+// responsible for closing the directory with [[Close_directory]].
+func Open_directory_readonly(path string) (Dir_t, error) {
+ _fd, err := syscall.Open(path, os.O_RDONLY|syscall.O_DIRECTORY, 0)
+ return Dir_t{fd: _fd, name: path}, err
+}
+
+// Close a directory returned by [[Open_directory_readonly]].
+func Close_directory(dir Dir_t) error {
+ return syscall.Close(dir.fd)
+}
+
+// syscallMode returns the syscall-specific mode flags from Go's portable mode flags.
+func syscallMode(i os.FileMode) (o uint32) {
+ // This part actually came from Go's os/file_posix.go but IMO it's too
+ // small and should fall under fair use.
+ o |= uint32(i.Perm())
+ if i&os.ModeSetuid != 0 {
+ o |= syscall.S_ISUID
+ }
+ if i&os.ModeSetgid != 0 {
+ o |= syscall.S_ISGID
+ }
+ if i&os.ModeSticky != 0 {
+ o |= syscall.S_ISVTX
+ }
+ // No mapping for Go's ModeTemporary (plan9 only).
+ return
+}
diff --git a/misc/openat2.go b/misc/openat2.go
index ab9f32d..2b63634 100644
--- a/misc/openat2.go
+++ b/misc/openat2.go
@@ -22,12 +22,12 @@ const (
)
// See openat2(2) on Linux
-func Openat2(dirfd int, path string, open_how *Open_how_t, size int) (fd int, err error) {
+func Openat2(dirfd int, path string, open_how *Open_how_t) (fd int, err error) {
path_ptr, err := String_to_byte_ptr(path)
if err != nil {
return
}
- _fd, _, errno := syscall.Syscall6(SYS_OPENAT2, uintptr(dirfd), uintptr(unsafe.Pointer(path_ptr)), uintptr(unsafe.Pointer(open_how)), uintptr(size), 0, 0)
+ _fd, _, errno := syscall.Syscall6(SYS_OPENAT2, uintptr(dirfd), uintptr(unsafe.Pointer(path_ptr)), uintptr(unsafe.Pointer(open_how)), uintptr(unsafe.Sizeof(Open_how_t{})), 0, 0)
fd = int(_fd)
if errno != 0 {
err = errno