main
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}