diff options
Diffstat (limited to 'src/internal/syscall/unix/fchmodat_linux.go')
| -rw-r--r-- | src/internal/syscall/unix/fchmodat_linux.go | 51 |
1 files changed, 51 insertions, 0 deletions
diff --git a/src/internal/syscall/unix/fchmodat_linux.go b/src/internal/syscall/unix/fchmodat_linux.go new file mode 100644 index 0000000000..786ec29df2 --- /dev/null +++ b/src/internal/syscall/unix/fchmodat_linux.go @@ -0,0 +1,51 @@ +// Copyright 2026 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build linux + +package unix + +import ( + "internal/strconv" + "syscall" +) + +func Fchmodat(dirfd int, path string, mode uint32, flags int) error { + // On Linux, the fchmodat syscall silently ignores the AT_SYMLINK_NOFOLLOW flag. + // We need to use fchmodat2 instead. + // syscall.Fchmodat handles this. + if err := syscall.Fchmodat(dirfd, path, mode, flags); err != syscall.EOPNOTSUPP { + return err + } + + // This kernel doesn't appear to support fchmodat2 (added in Linux 6.6). + // We can't fall back to Fchmod, because it requires write permissions on the file. + // Instead, use the same workaround as GNU libc and musl, which is to open the file + // and then fchmodat the FD in /proc/self/fd. + // See: https://lwn.net/Articles/939217/ + fd, err := Openat(dirfd, path, O_PATH|syscall.O_NOFOLLOW|syscall.O_CLOEXEC, 0) + if err != nil { + return err + } + defer syscall.Close(fd) + procPath := "/proc/self/fd/" + strconv.Itoa(fd) + + // Check to see if this file is a symlink. + // (We passed O_NOFOLLOW above, but O_PATH|O_NOFOLLOW will open a symlink.) + var st syscall.Stat_t + if err := syscall.Stat(procPath, &st); err != nil { + if err == syscall.ENOENT { + // /proc has probably not been mounted. Give up. + return syscall.EOPNOTSUPP + } + return err + } + if st.Mode&syscall.S_IFMT == syscall.S_IFLNK { + // fchmodat on the proc FD for a symlink apparently gives inconsistent + // results, so just refuse to try. + return syscall.EOPNOTSUPP + } + + return syscall.Fchmodat(AT_FDCWD, procPath, mode, flags&^AT_SYMLINK_NOFOLLOW) +} |
