Lindenii Project Forge
Login

go-lindenii-common

Common library for the Lindenii Project
Commit info
ID
b7dac177c81b1d1414a568c4375b24052045d4e6
Author
Runxi Yu <me@runxiyu.org>
Author date
Sat, 04 Jan 2025 20:40:00 +0800
Committer
Runxi Yu <me@runxiyu.org>
Committer date
Sat, 04 Jan 2025 20:41:42 +0800
Actions
misc: open_file
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
}
package misc

import (
	"syscall"
	"unsafe"
)

const SYS_OPENAT2 = 437

type Open_how_t struct {
	Flags   uint64
	Mode    uint64
	Resolve uint64
}

const (
	RESOLVE_BENEATH       = 0x8
	RESOLVE_IN_ROOT       = 0x10
	RESOLVE_NO_MAGICLINKS = 0x2
	RESOLVE_NO_SYMLINKS   = 0x4
	RESOLVE_NO_XDEV       = 0x1
)

// 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
	}
	return
}