From b7dac177c81b1d1414a568c4375b24052045d4e6 Mon Sep 17 00:00:00 2001 From: Runxi Yu Date: Sat, 4 Jan 2025 20:40:00 +0800 Subject: misc: open_file --- misc/open_file.go | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ misc/openat2.go | 4 +-- 2 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 misc/open_file.go (limited to 'misc') 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 -- cgit v1.2.3