FchmodFile is a wrapper around fchmodat2(AT_EMPTY_PATH) with fallbacks for older kernels. This is distinct from [File.Chmod] and [unix.Fchmod] in that it works on O_PATH file descriptors.
(f *os.File, mode uint32)
| 15 | // older kernels. This is distinct from [File.Chmod] and [unix.Fchmod] in that |
| 16 | // it works on O_PATH file descriptors. |
| 17 | func FchmodFile(f *os.File, mode uint32) error { |
| 18 | err := unix.Fchmodat(int(f.Fd()), "", mode, unix.AT_EMPTY_PATH) |
| 19 | // If fchmodat2(2) is not available at all, golang.org/x/unix (probably |
| 20 | // in order to mirror glibc) returns EOPNOTSUPP rather than EINVAL |
| 21 | // (what the kernel actually returns for invalid flags, which is being |
| 22 | // emulated) or ENOSYS (which is what glibc actually sees). |
| 23 | if err != unix.EINVAL && err != unix.EOPNOTSUPP { //nolint:errorlint // unix errors are bare |
| 24 | // err == nil is implicitly handled |
| 25 | return os.NewSyscallError("fchmodat2 AT_EMPTY_PATH", err) |
| 26 | } |
| 27 | |
| 28 | // AT_EMPTY_PATH support was added to fchmodat2 in Linux 6.6 |
| 29 | // (5daeb41a6fc9d0d81cb2291884b7410e062d8fa1). The alternative for |
| 30 | // older kernels is to go through /proc. |
| 31 | fdDir, closer, err2 := pathrs.ProcThreadSelfOpen("fd/", unix.O_DIRECTORY) |
| 32 | if err2 != nil { |
| 33 | return fmt.Errorf("fchmodat2 AT_EMPTY_PATH fallback: %w", err2) |
| 34 | } |
| 35 | defer closer() |
| 36 | defer fdDir.Close() |
| 37 | |
| 38 | err = unix.Fchmodat(int(fdDir.Fd()), strconv.Itoa(int(f.Fd())), mode, 0) |
| 39 | if err != nil { |
| 40 | err = fmt.Errorf("fchmodat /proc/self/fd/%d: %w", f.Fd(), err) |
| 41 | } |
| 42 | runtime.KeepAlive(f) |
| 43 | return err |
| 44 | } |
| 45 | |
| 46 | // FchownFile is a wrapper around fchownat(AT_EMPTY_PATH). This is distinct |
| 47 | // from [File.Chown] and [unix.Fchown] in that it works on O_PATH file |
no test coverage detected
searching dependent graphs…