aboutsummaryrefslogtreecommitdiff
path: root/misc/open_file.go
diff options
context:
space:
mode:
Diffstat (limited to 'misc/open_file.go')
-rw-r--r--misc/open_file.go75
1 files changed, 75 insertions, 0 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
+}