aboutsummaryrefslogblamecommitdiff
path: root/misc/open_file.go
blob: ec5d402c2233a589cd53fbfda0db575c9369c79c (plain) (tree)






























                                                                                                    
                                                       














                                                                        
                                                                
                                                          
                                                                                                 


                                              
                                                           
                                 



















                                                                                     
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)|syscall.O_CLOEXEC,
		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, syscall.O_RDONLY|syscall.O_DIRECTORY|syscall.O_CLOEXEC, 0)
	return Dir_t{fd: _fd, name: path}, err
}

// Close a directory returned by [Open_directory_readonly].
func (dir *Dir_t) Close() 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
}