2016-07-06 21:51:25 +02:00
|
|
|
// Package syscallcompat wraps Linux-specific syscalls.
|
2016-07-03 17:51:40 +02:00
|
|
|
package syscallcompat
|
2016-01-07 21:39:41 +01:00
|
|
|
|
2016-04-20 22:47:31 +02:00
|
|
|
import (
|
2019-01-06 19:49:03 +01:00
|
|
|
"fmt"
|
2019-01-12 20:42:05 +01:00
|
|
|
"runtime"
|
2016-04-20 22:47:31 +02:00
|
|
|
"sync"
|
|
|
|
"syscall"
|
2016-07-03 17:51:40 +02:00
|
|
|
|
2017-11-30 19:40:53 +01:00
|
|
|
"golang.org/x/sys/unix"
|
|
|
|
|
2017-12-03 17:57:08 +01:00
|
|
|
"github.com/hanwen/go-fuse/fuse"
|
|
|
|
|
2016-07-03 17:51:40 +02:00
|
|
|
"github.com/rfjakob/gocryptfs/internal/tlog"
|
2016-04-20 22:47:31 +02:00
|
|
|
)
|
|
|
|
|
2018-09-08 18:06:33 +02:00
|
|
|
const (
|
|
|
|
_FALLOC_FL_KEEP_SIZE = 0x01
|
2016-04-20 22:47:31 +02:00
|
|
|
|
2018-09-08 18:06:33 +02:00
|
|
|
// O_DIRECT means oncached I/O on Linux. No direct equivalent on MacOS and defined
|
|
|
|
// to zero there.
|
|
|
|
O_DIRECT = syscall.O_DIRECT
|
|
|
|
|
|
|
|
// O_PATH is only defined on Linux
|
|
|
|
O_PATH = unix.O_PATH
|
|
|
|
)
|
2018-07-04 09:04:00 +02:00
|
|
|
|
2016-04-20 22:47:31 +02:00
|
|
|
var preallocWarn sync.Once
|
2016-01-07 21:39:41 +01:00
|
|
|
|
2016-07-03 19:14:12 +02:00
|
|
|
// EnospcPrealloc preallocates ciphertext space without changing the file
|
|
|
|
// size. This guarantees that we don't run out of space while writing a
|
|
|
|
// ciphertext block (that would corrupt the block).
|
|
|
|
func EnospcPrealloc(fd int, off int64, len int64) (err error) {
|
2016-01-07 21:39:41 +01:00
|
|
|
for {
|
2016-10-04 23:30:05 +02:00
|
|
|
err = syscall.Fallocate(fd, _FALLOC_FL_KEEP_SIZE, off, len)
|
2016-01-07 21:39:41 +01:00
|
|
|
if err == syscall.EINTR {
|
|
|
|
// fallocate, like many syscalls, can return EINTR. This is not an
|
|
|
|
// error and just signifies that the operation was interrupted by a
|
|
|
|
// signal and we should try again.
|
|
|
|
continue
|
|
|
|
}
|
2016-04-20 22:47:31 +02:00
|
|
|
if err == syscall.EOPNOTSUPP {
|
2016-07-03 19:14:12 +02:00
|
|
|
// ZFS and ext3 do not support fallocate. Warn but continue anyway.
|
|
|
|
// https://github.com/rfjakob/gocryptfs/issues/22
|
2016-04-20 22:47:31 +02:00
|
|
|
preallocWarn.Do(func() {
|
2016-06-15 23:30:44 +02:00
|
|
|
tlog.Warn.Printf("Warning: The underlying filesystem " +
|
2016-04-20 22:47:31 +02:00
|
|
|
"does not support fallocate(2). gocryptfs will continue working " +
|
|
|
|
"but is no longer resistant against out-of-space errors.\n")
|
|
|
|
})
|
|
|
|
return nil
|
|
|
|
}
|
2016-01-07 21:39:41 +01:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
2016-07-03 19:14:12 +02:00
|
|
|
|
2016-10-02 06:14:18 +02:00
|
|
|
// Fallocate wraps the Fallocate syscall.
|
2016-07-03 20:05:32 +02:00
|
|
|
func Fallocate(fd int, mode uint32, off int64, len int64) (err error) {
|
|
|
|
return syscall.Fallocate(fd, mode, off, len)
|
|
|
|
}
|
|
|
|
|
2019-01-12 20:42:05 +01:00
|
|
|
// OpenatUser runs the Openat syscall in the context of a different user.
|
|
|
|
func OpenatUser(dirfd int, path string, flags int, mode uint32, context *fuse.Context) (fd int, err error) {
|
|
|
|
if context != nil {
|
|
|
|
runtime.LockOSThread()
|
|
|
|
defer runtime.UnlockOSThread()
|
|
|
|
|
|
|
|
err = syscall.Setregid(-1, int(context.Owner.Gid))
|
|
|
|
if err != nil {
|
|
|
|
return -1, err
|
|
|
|
}
|
|
|
|
defer syscall.Setregid(-1, 0)
|
|
|
|
|
|
|
|
err = syscall.Setreuid(-1, int(context.Owner.Uid))
|
|
|
|
if err != nil {
|
|
|
|
return -1, err
|
|
|
|
}
|
|
|
|
defer syscall.Setreuid(-1, 0)
|
|
|
|
}
|
|
|
|
|
|
|
|
return Openat(dirfd, path, flags, mode)
|
|
|
|
}
|
|
|
|
|
2016-10-02 06:14:18 +02:00
|
|
|
// Mknodat wraps the Mknodat syscall.
|
2016-07-03 20:21:29 +02:00
|
|
|
func Mknodat(dirfd int, path string, mode uint32, dev int) (err error) {
|
|
|
|
return syscall.Mknodat(dirfd, path, mode, dev)
|
|
|
|
}
|
2017-06-18 15:40:38 +02:00
|
|
|
|
2019-01-12 21:19:23 +01:00
|
|
|
// MknodatUser runs the Mknodat syscall in the context of a different user.
|
|
|
|
func MknodatUser(dirfd int, path string, mode uint32, dev int, context *fuse.Context) (err error) {
|
|
|
|
if context != nil {
|
|
|
|
runtime.LockOSThread()
|
|
|
|
defer runtime.UnlockOSThread()
|
|
|
|
|
|
|
|
err = syscall.Setregid(-1, int(context.Owner.Gid))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer syscall.Setregid(-1, 0)
|
|
|
|
|
|
|
|
err = syscall.Setreuid(-1, int(context.Owner.Uid))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer syscall.Setreuid(-1, 0)
|
|
|
|
}
|
|
|
|
|
|
|
|
return Mknodat(dirfd, path, mode, dev)
|
|
|
|
}
|
|
|
|
|
2017-06-18 15:40:38 +02:00
|
|
|
// Dup3 wraps the Dup3 syscall. We want to use Dup3 rather than Dup2 because Dup2
|
|
|
|
// is not implemented on arm64.
|
|
|
|
func Dup3(oldfd int, newfd int, flags int) (err error) {
|
|
|
|
return syscall.Dup3(oldfd, newfd, flags)
|
|
|
|
}
|
2017-11-26 21:59:24 +01:00
|
|
|
|
2017-11-29 12:54:34 +01:00
|
|
|
// Fchmodat syscall.
|
|
|
|
func Fchmodat(dirfd int, path string, mode uint32, flags int) (err error) {
|
2019-01-06 19:49:03 +01:00
|
|
|
// Linux does not support passing flags to Fchmodat! From the man page:
|
2018-08-26 13:00:00 +02:00
|
|
|
// 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.
|
2019-01-06 19:49:03 +01:00
|
|
|
|
|
|
|
// 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)
|
2017-11-29 12:54:34 +01:00
|
|
|
}
|
|
|
|
|
2017-11-26 21:59:24 +01:00
|
|
|
// Fchownat syscall.
|
|
|
|
func Fchownat(dirfd int, path string, uid int, gid int, flags int) (err error) {
|
2017-11-30 19:40:53 +01:00
|
|
|
// Why would we ever want to call this without AT_SYMLINK_NOFOLLOW?
|
|
|
|
if flags&unix.AT_SYMLINK_NOFOLLOW == 0 {
|
|
|
|
tlog.Warn.Printf("Fchownat: adding missing AT_SYMLINK_NOFOLLOW flag")
|
|
|
|
flags |= unix.AT_SYMLINK_NOFOLLOW
|
|
|
|
}
|
2017-11-26 21:59:24 +01:00
|
|
|
return syscall.Fchownat(dirfd, path, uid, gid, flags)
|
|
|
|
}
|
2017-11-28 00:54:38 +01:00
|
|
|
|
2017-12-02 18:32:25 +01:00
|
|
|
// Symlinkat syscall.
|
2017-11-28 00:54:38 +01:00
|
|
|
func Symlinkat(oldpath string, newdirfd int, newpath string) (err error) {
|
2017-12-02 18:32:25 +01:00
|
|
|
return unix.Symlinkat(oldpath, newdirfd, newpath)
|
2017-11-28 00:54:38 +01:00
|
|
|
}
|
2017-11-29 13:21:28 +01:00
|
|
|
|
2019-01-12 21:22:52 +01:00
|
|
|
// SymlinkatUser runs the Symlinkat syscall in the context of a different user.
|
|
|
|
func SymlinkatUser(oldpath string, newdirfd int, newpath string, context *fuse.Context) (err error) {
|
|
|
|
if context != nil {
|
|
|
|
runtime.LockOSThread()
|
|
|
|
defer runtime.UnlockOSThread()
|
|
|
|
|
|
|
|
err = syscall.Setregid(-1, int(context.Owner.Gid))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer syscall.Setregid(-1, 0)
|
|
|
|
|
|
|
|
err = syscall.Setreuid(-1, int(context.Owner.Uid))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer syscall.Setreuid(-1, 0)
|
|
|
|
}
|
|
|
|
|
|
|
|
return Symlinkat(oldpath, newdirfd, newpath)
|
|
|
|
}
|
|
|
|
|
2017-11-29 13:21:28 +01:00
|
|
|
// Mkdirat syscall.
|
|
|
|
func Mkdirat(dirfd int, path string, mode uint32) (err error) {
|
|
|
|
return syscall.Mkdirat(dirfd, path, mode)
|
|
|
|
}
|
2017-12-03 17:53:14 +01:00
|
|
|
|
2019-01-12 20:57:31 +01:00
|
|
|
// MkdiratUser runs the Mkdirat syscall in the context of a different user.
|
|
|
|
func MkdiratUser(dirfd int, path string, mode uint32, context *fuse.Context) (err error) {
|
|
|
|
if context != nil {
|
|
|
|
runtime.LockOSThread()
|
|
|
|
defer runtime.UnlockOSThread()
|
|
|
|
|
|
|
|
err = syscall.Setregid(-1, int(context.Owner.Gid))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer syscall.Setregid(-1, 0)
|
|
|
|
|
|
|
|
err = syscall.Setreuid(-1, int(context.Owner.Uid))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer syscall.Setreuid(-1, 0)
|
|
|
|
}
|
|
|
|
|
|
|
|
return Mkdirat(dirfd, path, mode)
|
|
|
|
}
|
|
|
|
|
2017-12-03 17:53:14 +01:00
|
|
|
// Fstatat syscall.
|
|
|
|
func Fstatat(dirfd int, path string, stat *unix.Stat_t, flags int) (err error) {
|
|
|
|
// Why would we ever want to call this without AT_SYMLINK_NOFOLLOW?
|
|
|
|
if flags&unix.AT_SYMLINK_NOFOLLOW == 0 {
|
|
|
|
tlog.Warn.Printf("Fstatat: adding missing AT_SYMLINK_NOFOLLOW flag")
|
|
|
|
flags |= unix.AT_SYMLINK_NOFOLLOW
|
|
|
|
}
|
|
|
|
return unix.Fstatat(dirfd, path, stat, flags)
|
|
|
|
}
|
2017-12-03 17:57:08 +01:00
|
|
|
|
|
|
|
// Getdents syscall.
|
|
|
|
func Getdents(fd int) ([]fuse.DirEntry, error) {
|
|
|
|
return getdents(fd)
|
|
|
|
}
|