diff options
| author | Sebastian Lackner | 2019-01-06 19:49:03 +0100 | 
|---|---|---|
| committer | Jakob Unterwurzacher | 2019-01-07 23:07:53 +0100 | 
| commit | 4170ef00f32b3943a75f1c85c2b21dbe27ba30cd (patch) | |
| tree | 0633915083c7748ac7a6e2bde16b29ac688cf1ed /internal | |
| parent | 8253c55386e59295ea48dfc1b9beea4d785fcbbd (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.go | 36 | 
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. | 
