main
Raw Download raw file
 1//go:build linux
 2
 3// Copyright (C) 2024 SUSE LLC. All rights reserved.
 4// Use of this source code is governed by a BSD-style
 5// license that can be found in the LICENSE file.
 6
 7package securejoin
 8
 9import (
10	"os"
11	"path/filepath"
12
13	"golang.org/x/sys/unix"
14)
15
16func dupFile(f *os.File) (*os.File, error) {
17	fd, err := unix.FcntlInt(f.Fd(), unix.F_DUPFD_CLOEXEC, 0)
18	if err != nil {
19		return nil, os.NewSyscallError("fcntl(F_DUPFD_CLOEXEC)", err)
20	}
21	return os.NewFile(uintptr(fd), f.Name()), nil
22}
23
24func openatFile(dir *os.File, path string, flags int, mode int) (*os.File, error) {
25	// Make sure we always set O_CLOEXEC.
26	flags |= unix.O_CLOEXEC
27	fd, err := unix.Openat(int(dir.Fd()), path, flags, uint32(mode))
28	if err != nil {
29		return nil, &os.PathError{Op: "openat", Path: dir.Name() + "/" + path, Err: err}
30	}
31	// All of the paths we use with openatFile(2) are guaranteed to be
32	// lexically safe, so we can use path.Join here.
33	fullPath := filepath.Join(dir.Name(), path)
34	return os.NewFile(uintptr(fd), fullPath), nil
35}
36
37func fstatatFile(dir *os.File, path string, flags int) (unix.Stat_t, error) {
38	var stat unix.Stat_t
39	if err := unix.Fstatat(int(dir.Fd()), path, &stat, flags); err != nil {
40		return stat, &os.PathError{Op: "fstatat", Path: dir.Name() + "/" + path, Err: err}
41	}
42	return stat, nil
43}
44
45func readlinkatFile(dir *os.File, path string) (string, error) {
46	size := 4096
47	for {
48		linkBuf := make([]byte, size)
49		n, err := unix.Readlinkat(int(dir.Fd()), path, linkBuf)
50		if err != nil {
51			return "", &os.PathError{Op: "readlinkat", Path: dir.Name() + "/" + path, Err: err}
52		}
53		if n != size {
54			return string(linkBuf[:n]), nil
55		}
56		// Possible truncation, resize the buffer.
57		size *= 2
58	}
59}