aboutsummaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
authorSebastian Lackner2019-01-06 19:49:03 +0100
committerJakob Unterwurzacher2019-01-07 23:07:53 +0100
commit4170ef00f32b3943a75f1c85c2b21dbe27ba30cd (patch)
tree0633915083c7748ac7a6e2bde16b29ac688cf1ed /internal
parent8253c55386e59295ea48dfc1b9beea4d785fcbbd (diff)
syscallcompat: Implement workaround for Fchmodat with AT_SYMLINK_NOFOLLOW.
Fixes https://github.com/rfjakob/gocryptfs/issues/259
Diffstat (limited to 'internal')
-rw-r--r--internal/syscallcompat/sys_linux.go36
1 files changed, 34 insertions, 2 deletions
diff --git a/internal/syscallcompat/sys_linux.go b/internal/syscallcompat/sys_linux.go
index 6b75957..5c180ec 100644
--- a/internal/syscallcompat/sys_linux.go
+++ b/internal/syscallcompat/sys_linux.go
@@ -2,6 +2,7 @@
package syscallcompat
import (
+ "fmt"
"sync"
"syscall"
@@ -97,12 +98,43 @@ func Dup3(oldfd int, newfd int, flags int) (err error) {
// Fchmodat syscall.
func Fchmodat(dirfd int, path string, mode uint32, flags int) (err error) {
- // Linux does not support passing flags to fchmodat! From the man page:
+ // Linux does not support passing flags to Fchmodat! From the man page:
// AT_SYMLINK_NOFOLLOW ... This flag is not currently implemented.
// Linux ignores any flags, but Go stdlib rejects them with EOPNOTSUPP starting
// with Go 1.11. See https://github.com/golang/go/issues/20130 for more info.
// TODO: Use fchmodat2 once available on Linux.
- return syscall.Fchmodat(dirfd, path, mode, 0)
+
+ // Why would we ever want to call this without AT_SYMLINK_NOFOLLOW?
+ if flags&unix.AT_SYMLINK_NOFOLLOW == 0 {
+ tlog.Warn.Printf("Fchmodat: adding missing AT_SYMLINK_NOFOLLOW flag")
+ }
+
+ // Open handle to the filename (but without opening the actual file).
+ fd, err := syscall.Openat(dirfd, path, syscall.O_NOFOLLOW|O_PATH, 0)
+ if err != nil {
+ return err
+ }
+ defer syscall.Close(fd)
+
+ // Now we can check the type without the risk of race-conditions.
+ var st syscall.Stat_t
+ err = syscall.Fstat(fd, &st)
+ if err != nil {
+ return err
+ }
+
+ // Return syscall.ELOOP if path refers to a symlink.
+ var a fuse.Attr
+ a.FromStat(&st)
+ if a.IsSymlink() {
+ return syscall.ELOOP
+ }
+
+ // Change mode of the actual file. Note that we can neither use
+ // Fchmodat (since fd is not necessarily a directory) nor Fchmod
+ // (since we are using O_PATH).
+ procPath := fmt.Sprintf("/proc/self/fd/%d", fd)
+ return syscall.Chmod(procPath, mode)
}
// Fchownat syscall.